Add Jackson 3 support
This commit adds support for Jackson 3 which has the following major differences with the Jackson 2 one: - jackson subpackage instead of jackson2 - Jackson type prefix instead of Jackson2 - JsonMapper instead of ObjectMapper - For configuration, JsonMapper.Builder instead of ObjectMapper since the latter is now immutable - Remove custom support for unmodifiable collections - Use safe default typing via a PolymorphicTypeValidator Jackson 3 changes compared to Jackson 2 are documented in https://cowtowncoder.medium.com/jackson-3-0-0-ga-released-1f669cda529a and https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md. This commit does not cover webauthn which is a special case (uses jackson sub-package for Jackson 2 support) which will be handled in a distinct commit. See gh-17832 Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
This commit is contained in:
committed by
Rob Winch
parent
916a687b29
commit
65a14d6c6d
+2
-1
@@ -11,10 +11,11 @@ dependencies {
|
||||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
api "com.nimbusds:nimbus-jose-jwt"
|
||||
api "com.fasterxml.jackson.core:jackson-databind"
|
||||
api 'tools.jackson.core:jackson-databind'
|
||||
|
||||
optional "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
|
||||
optional "org.springframework:spring-jdbc"
|
||||
optional "com.fasterxml.jackson.core:jackson-databind"
|
||||
|
||||
testImplementation project(":spring-security-test")
|
||||
testImplementation project(path : ':spring-security-oauth2-jose', configuration : 'tests')
|
||||
|
||||
+91
-24
@@ -33,13 +33,15 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
@@ -64,8 +66,10 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -469,16 +473,12 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
private Mapper mapper = (ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper",
|
||||
OAuth2AuthorizationRowMapper.class.getClassLoader())) ? new JacksonDelegate() : new Jackson2Delegate();
|
||||
|
||||
public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
this.registeredClientRepository = registeredClientRepository;
|
||||
|
||||
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -623,9 +623,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
this.lobHandler = lobHandler;
|
||||
}
|
||||
|
||||
public final void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper cannot be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public final void setMapper(Mapper mapper) {
|
||||
Assert.notNull(mapper, "objectMapper cannot be null");
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
protected final RegisteredClientRepository getRegisteredClientRepository() {
|
||||
@@ -636,13 +636,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return this.lobHandler;
|
||||
}
|
||||
|
||||
protected final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
protected final Mapper getMapper() {
|
||||
return this.mapper;
|
||||
}
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return this.objectMapper.readValue(data, new TypeReference<>() {
|
||||
return this.mapper.readValue(data, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@@ -659,13 +659,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
public static class OAuth2AuthorizationParametersMapper
|
||||
implements Function<OAuth2Authorization, List<SqlParameterValue>> {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
private Mapper mapper = (ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper",
|
||||
OAuth2AuthorizationRowMapper.class.getClassLoader())) ? new JacksonDelegate() : new Jackson2Delegate();
|
||||
|
||||
public OAuth2AuthorizationParametersMapper() {
|
||||
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -737,13 +734,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public final void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper cannot be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public final void setMapper(Mapper mapper) {
|
||||
Assert.notNull(mapper, "mapper cannot be null");
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
protected final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
protected final Mapper getMapper() {
|
||||
return this.mapper;
|
||||
}
|
||||
|
||||
private <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(String tokenColumnName,
|
||||
@@ -774,7 +771,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
return this.mapper.writeValueAsString(data);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
@@ -851,4 +848,74 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
}
|
||||
|
||||
public interface Mapper {
|
||||
|
||||
String writeValueAsString(Object data);
|
||||
|
||||
<T> T readValue(String value, ParameterizedTypeReference<T> typeReference);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public static class Jackson2Delegate implements Mapper {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public Jackson2Delegate() {
|
||||
ClassLoader classLoader = Jackson2Delegate.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeValueAsString(Object data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
}
|
||||
catch (JsonProcessingException ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readValue(String value, ParameterizedTypeReference<T> typeReference) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.JavaType javaType = this.objectMapper.getTypeFactory()
|
||||
.constructType(typeReference.getType());
|
||||
return this.objectMapper.readValue(value, javaType);
|
||||
}
|
||||
catch (JsonProcessingException ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class JacksonDelegate implements Mapper {
|
||||
|
||||
private final JsonMapper jsonMapper;
|
||||
|
||||
public JacksonDelegate() {
|
||||
this.jsonMapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
public JacksonDelegate(JsonMapper.Builder builder) {
|
||||
this.jsonMapper = builder.addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeValueAsString(Object data) {
|
||||
return this.jsonMapper.writeValueAsString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readValue(String value, ParameterizedTypeReference<T> typeReference) {
|
||||
tools.jackson.databind.JavaType javaType = this.jsonMapper.getTypeFactory()
|
||||
.constructType(typeReference.getType());
|
||||
return this.jsonMapper.readValue(value, javaType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* Utility class for {@code JsonNode}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class JsonNodeUtils {
|
||||
|
||||
static final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isString()) ? value.stringValue() : null;
|
||||
}
|
||||
|
||||
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
||||
DeserializationContext context) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isContainer())
|
||||
? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;
|
||||
}
|
||||
|
||||
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isObject()) ? value : null;
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link SignatureAlgorithm}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see SignatureAlgorithm
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class JwsAlgorithmMixin {
|
||||
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.exc.InvalidFormatException;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestMixin
|
||||
*/
|
||||
final class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {
|
||||
|
||||
@Override
|
||||
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode root = context.readTree(parser);
|
||||
return deserialize(parser, context, root);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {
|
||||
AuthorizationGrantType authorizationGrantType = convertAuthorizationGrantType(
|
||||
JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
|
||||
Builder builder = getBuilder(parser, authorizationGrantType);
|
||||
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
|
||||
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
|
||||
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
|
||||
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, context));
|
||||
builder.state(JsonNodeUtils.findStringValue(root, "state"));
|
||||
builder.additionalParameters(
|
||||
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
|
||||
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||
return OAuth2AuthorizationRequest.authorizationCode();
|
||||
}
|
||||
throw new InvalidFormatException(parser, "Invalid authorizationGrantType", authorizationGrantType,
|
||||
AuthorizationGrantType.class);
|
||||
}
|
||||
|
||||
private static AuthorizationGrantType convertAuthorizationGrantType(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
|
||||
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestDeserializer
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizationRequestMixin {
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.DefaultTyping;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.jackson.CoreJacksonModule;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
|
||||
|
||||
/**
|
||||
* Jackson {@code Module} for {@code spring-security-oauth2-authorization-server}, that
|
||||
* registers the following mix-in annotations:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link OAuth2TokenExchangeActor}</li>
|
||||
* <li>{@link OAuth2AuthorizationRequestMixin}</li>
|
||||
* <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>
|
||||
* <li>{@link JwsAlgorithmMixin}</li>
|
||||
* <li>{@link OAuth2TokenFormatMixin}</li>
|
||||
* </ul>
|
||||
*
|
||||
* If not already enabled, default typing will be automatically enabled as type info is
|
||||
* required to properly serialize/deserialize objects. In order to use this module just
|
||||
* add it to your {@code JsonMapper.Builder} configuration.
|
||||
*
|
||||
* <pre>
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(new OAuth2AuthorizationServerJacksonModule()).build;
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
|
||||
|
||||
public OAuth2AuthorizationServerJacksonModule() {
|
||||
super(OAuth2AuthorizationServerJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
super.configurePolymorphicTypeValidator(builder);
|
||||
builder.allowIfSubType(OAuth2TokenFormat.class)
|
||||
.allowIfSubType(OAuth2TokenExchangeActor.class)
|
||||
.allowIfSubType(OAuth2TokenExchangeCompositeAuthenticationToken.class)
|
||||
.allowIfSubType(SignatureAlgorithm.class)
|
||||
.allowIfSubType(MacAlgorithm.class)
|
||||
.allowIfSubType(OAuth2AuthorizationRequest.class)
|
||||
.allowIfSubType(URL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
super.setupModule(context);
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder();
|
||||
this.configurePolymorphicTypeValidator(builder);
|
||||
((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(), DefaultTyping.NON_FINAL,
|
||||
JsonTypeInfo.As.PROPERTY);
|
||||
context.setMixIn(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);
|
||||
context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
|
||||
context.setMixIn(OAuth2TokenExchangeCompositeAuthenticationToken.class,
|
||||
OAuth2TokenExchangeCompositeAuthenticationTokenMixin.class);
|
||||
context.setMixIn(SignatureAlgorithm.class, JwsAlgorithmMixin.class);
|
||||
context.setMixIn(MacAlgorithm.class, JwsAlgorithmMixin.class);
|
||||
context.setMixIn(OAuth2TokenFormat.class, OAuth2TokenFormatMixin.class);
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeActor}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenExchangeActor
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenExchangeActorMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenExchangeActorMixin(@JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize
|
||||
* {@link OAuth2TokenExchangeCompositeAuthenticationToken}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenExchangeCompositeAuthenticationToken
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenExchangeCompositeAuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenExchangeCompositeAuthenticationTokenMixin(@JsonProperty("subject") Authentication subject,
|
||||
@JsonProperty("actors") List<Authentication> actors) {
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2TokenFormat}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenFormat
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenFormatMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenFormatMixin(@JsonProperty("value") String value) {
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -29,11 +29,11 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@@ -747,7 +747,7 @@ public class JdbcOAuth2AuthorizationServiceTests {
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return getObjectMapper().readValue(data, new TypeReference<>() {
|
||||
return getMapper().readValue(data, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@@ -852,7 +852,7 @@ public class JdbcOAuth2AuthorizationServiceTests {
|
||||
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return getObjectMapper().writeValueAsString(data);
|
||||
return getMapper().writeValueAsString(data);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
|
||||
+1
@@ -365,6 +365,7 @@ public class JdbcRegisteredClientRepositoryTests {
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static final class CustomRegisteredClientRowMapper implements RowMapper<RegisteredClient> {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2AuthorizationServerJackson2Module}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public class OAuth2AuthorizationServerJacksonModuleTests {
|
||||
|
||||
private static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.mapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2AuthorizationAttributesThenSuccess() {
|
||||
Authentication principal = new UsernamePasswordAuthenticationToken("principal", "credentials");
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization()
|
||||
.attributes((attrs) -> attrs.put(Principal.class.getName(), principal))
|
||||
.build();
|
||||
Map<String, Object> attributes = authorization.getAttributes();
|
||||
String json = this.mapper.writeValueAsString(attributes);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2AccessTokenMetadataThenSuccess() {
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();
|
||||
Map<String, Object> metadata = authorization.getAccessToken().getMetadata();
|
||||
String json = this.mapper.writeValueAsString(metadata);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenClientSettingsThenSuccess() {
|
||||
ClientSettings clientSettings = ClientSettings.builder()
|
||||
.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)
|
||||
.build();
|
||||
Map<String, Object> clientSettingsMap = clientSettings.getSettings();
|
||||
String json = this.mapper.writeValueAsString(clientSettingsMap);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(clientSettingsMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenTokenSettingsThenSuccess() {
|
||||
TokenSettings tokenSettings = TokenSettings.builder().build();
|
||||
Map<String, Object> tokenSettingsMap = tokenSettings.getSettings();
|
||||
String json = this.mapper.writeValueAsString(tokenSettingsMap);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(tokenSettingsMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2TokenExchangeCompositeAuthenticationTokenThenSuccess() {
|
||||
Authentication subject = new UsernamePasswordAuthenticationToken("principal", "credentials");
|
||||
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(
|
||||
Map.of(JwtClaimNames.ISS, "issuer-1", JwtClaimNames.SUB, "actor1"));
|
||||
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(
|
||||
Map.of(JwtClaimNames.ISS, "issuer-2", JwtClaimNames.SUB, "actor2"));
|
||||
OAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(
|
||||
subject, List.of(actor1, actor2));
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
assertThat(this.mapper.readValue(json, OAuth2TokenExchangeCompositeAuthenticationToken.class))
|
||||
.isEqualTo(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link TestingAuthenticationToken}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see TestingAuthenticationToken
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true)
|
||||
public class TestingAuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
TestingAuthenticationTokenMixin(@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("credentials") Object credentials,
|
||||
@JsonProperty("authorities") List<GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@ dependencies {
|
||||
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
optional 'org.springframework:spring-jdbc'
|
||||
optional 'org.springframework:spring-r2dbc'
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
|
||||
testImplementation project(path: ':spring-security-oauth2-core', configuration: 'tests')
|
||||
testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link ClientRegistration}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see ClientRegistration
|
||||
* @see ClientRegistrationMixin
|
||||
*/
|
||||
final class ClientRegistrationDeserializer extends ValueDeserializer<ClientRegistration> {
|
||||
|
||||
private static final StdConverter<JsonNode, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_CONVERTER = new StdConverters.ClientAuthenticationMethodConverter();
|
||||
|
||||
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();
|
||||
|
||||
private static final StdConverter<JsonNode, AuthenticationMethod> AUTHENTICATION_METHOD_CONVERTER = new StdConverters.AuthenticationMethodConverter();
|
||||
|
||||
@Override
|
||||
public ClientRegistration deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode clientRegistrationNode = context.readTree(parser);
|
||||
JsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, "providerDetails");
|
||||
JsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, "userInfoEndpoint");
|
||||
return ClientRegistration
|
||||
.withRegistrationId(JsonNodeUtils.findStringValue(clientRegistrationNode, "registrationId"))
|
||||
.clientId(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientId"))
|
||||
.clientSecret(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientSecret"))
|
||||
.clientAuthenticationMethod(CLIENT_AUTHENTICATION_METHOD_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "clientAuthenticationMethod")))
|
||||
.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "authorizationGrantType")))
|
||||
.redirectUri(JsonNodeUtils.findStringValue(clientRegistrationNode, "redirectUri"))
|
||||
.scope(JsonNodeUtils.findValue(clientRegistrationNode, "scopes", JsonNodeUtils.STRING_SET, context))
|
||||
.clientName(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientName"))
|
||||
.authorizationUri(JsonNodeUtils.findStringValue(providerDetailsNode, "authorizationUri"))
|
||||
.tokenUri(JsonNodeUtils.findStringValue(providerDetailsNode, "tokenUri"))
|
||||
.userInfoUri(JsonNodeUtils.findStringValue(userInfoEndpointNode, "uri"))
|
||||
.userInfoAuthenticationMethod(AUTHENTICATION_METHOD_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(userInfoEndpointNode, "authenticationMethod")))
|
||||
.userNameAttributeName(JsonNodeUtils.findStringValue(userInfoEndpointNode, "userNameAttributeName"))
|
||||
.jwkSetUri(JsonNodeUtils.findStringValue(providerDetailsNode, "jwkSetUri"))
|
||||
.issuerUri(JsonNodeUtils.findStringValue(providerDetailsNode, "issuerUri"))
|
||||
.providerConfigurationMetadata(JsonNodeUtils.findValue(providerDetailsNode, "configurationMetadata",
|
||||
JsonNodeUtils.STRING_OBJECT_MAP, context))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link ClientRegistration}. It also
|
||||
* registers a custom deserializer {@link ClientRegistrationDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see ClientRegistration
|
||||
* @see ClientRegistrationDeserializer
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = ClientRegistrationDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class ClientRegistrationMixin {
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see DefaultOAuth2User
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class DefaultOAuth2UserMixin {
|
||||
|
||||
@JsonCreator
|
||||
DefaultOAuth2UserMixin(@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes,
|
||||
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see DefaultOidcUser
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "attributes" })
|
||||
abstract class DefaultOidcUserMixin {
|
||||
|
||||
@JsonCreator
|
||||
DefaultOidcUserMixin(@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("idToken") OidcIdToken idToken, @JsonProperty("userInfo") OidcUserInfo userInfo,
|
||||
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* Utility class for {@code JsonNode}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class JsonNodeUtils {
|
||||
|
||||
static final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isString()) ? value.stringValue() : null;
|
||||
}
|
||||
|
||||
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
||||
DeserializationContext context) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isContainer())
|
||||
? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;
|
||||
}
|
||||
|
||||
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isObject()) ? value : null;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AccessToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AccessToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AccessTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AccessTokenMixin(
|
||||
@JsonProperty("tokenType") @JsonDeserialize(
|
||||
converter = StdConverters.AccessTokenTypeConverter.class) OAuth2AccessToken.TokenType tokenType,
|
||||
@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt,
|
||||
@JsonProperty("expiresAt") Instant expiresAt, @JsonProperty("scopes") Set<String> scopes) {
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
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.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize
|
||||
* {@link OAuth2AuthenticationException}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Dennis Neufeld
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthenticationException
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "cause", "stackTrace", "suppressedExceptions" })
|
||||
abstract class OAuth2AuthenticationExceptionMixin {
|
||||
|
||||
@JsonProperty("error")
|
||||
abstract OAuth2Error getError();
|
||||
|
||||
@JsonProperty("detailMessage")
|
||||
abstract String getMessage();
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthenticationExceptionMixin(@JsonProperty("error") OAuth2Error error,
|
||||
@JsonProperty("detailMessage") String message) {
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthenticationToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0O
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "authenticated" })
|
||||
abstract class OAuth2AuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthenticationTokenMixin(@JsonProperty("principal") OAuth2User principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("authorizedClientRegistrationId") String authorizedClientRegistrationId) {
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestMixin
|
||||
*/
|
||||
final class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {
|
||||
|
||||
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();
|
||||
|
||||
@Override
|
||||
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode root = context.readTree(parser);
|
||||
return deserialize(parser, context, root);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {
|
||||
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
|
||||
Builder builder = getBuilder(parser, authorizationGrantType);
|
||||
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
|
||||
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
|
||||
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
|
||||
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, context));
|
||||
builder.state(JsonNodeUtils.findStringValue(root, "state"));
|
||||
builder.additionalParameters(
|
||||
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
|
||||
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||
return OAuth2AuthorizationRequest.authorizationCode();
|
||||
}
|
||||
throw new StreamReadException(parser, "Invalid authorizationGrantType");
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
|
||||
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestDeserializer
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizationRequestMixin {
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizedClient}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizedClientMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthorizedClientMixin(@JsonProperty("clientRegistration") ClientRegistration clientRegistration,
|
||||
@JsonProperty("principalName") String principalName,
|
||||
@JsonProperty("accessToken") OAuth2AccessToken accessToken,
|
||||
@JsonProperty("refreshToken") OAuth2RefreshToken refreshToken) {
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModule;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
/**
|
||||
* Jackson {@code Module} for {@code spring-security-oauth2-client}, that registers the
|
||||
* following mix-in annotations:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link OAuth2AuthorizationRequestMixin}</li>
|
||||
* <li>{@link ClientRegistrationMixin}</li>
|
||||
* <li>{@link OAuth2AccessTokenMixin}</li>
|
||||
* <li>{@link OAuth2RefreshTokenMixin}</li>
|
||||
* <li>{@link OAuth2AuthorizedClientMixin}</li>
|
||||
* <li>{@link OAuth2UserAuthorityMixin}</li>
|
||||
* <li>{@link DefaultOAuth2UserMixin}</li>
|
||||
* <li>{@link OidcIdTokenMixin}</li>
|
||||
* <li>{@link OidcUserInfoMixin}</li>
|
||||
* <li>{@link OidcUserAuthorityMixin}</li>
|
||||
* <li>{@link DefaultOidcUserMixin}</li>
|
||||
* <li>{@link OAuth2AuthenticationTokenMixin}</li>
|
||||
* <li>{@link OAuth2AuthenticationExceptionMixin}</li>
|
||||
* <li>{@link OAuth2ErrorMixin}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OAuth2ClientJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public OAuth2ClientJacksonModule() {
|
||||
super(OAuth2ClientJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(OAuth2AuthenticationException.class)
|
||||
.allowIfSubType(DefaultOidcUser.class)
|
||||
.allowIfSubType(OAuth2AuthorizationRequest.class)
|
||||
.allowIfSubType(OAuth2Error.class)
|
||||
.allowIfSubType(OAuth2AuthorizedClient.class)
|
||||
.allowIfSubType(OidcIdToken.class)
|
||||
.allowIfSubType(OidcUserInfo.class)
|
||||
.allowIfSubType(DefaultOAuth2User.class)
|
||||
.allowIfSubType(ClientRegistration.class)
|
||||
.allowIfSubType(OAuth2AccessToken.class)
|
||||
.allowIfSubType(OAuth2RefreshToken.class)
|
||||
.allowIfSubType(OAuth2AuthenticationToken.class)
|
||||
.allowIfSubType(OidcUserAuthority.class)
|
||||
.allowIfSubType(OAuth2UserAuthority.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
|
||||
context.setMixIn(ClientRegistration.class, ClientRegistrationMixin.class);
|
||||
context.setMixIn(OAuth2AccessToken.class, OAuth2AccessTokenMixin.class);
|
||||
context.setMixIn(OAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);
|
||||
context.setMixIn(OAuth2AuthorizedClient.class, OAuth2AuthorizedClientMixin.class);
|
||||
context.setMixIn(OAuth2UserAuthority.class, OAuth2UserAuthorityMixin.class);
|
||||
context.setMixIn(DefaultOAuth2User.class, DefaultOAuth2UserMixin.class);
|
||||
context.setMixIn(OidcIdToken.class, OidcIdTokenMixin.class);
|
||||
context.setMixIn(OidcUserInfo.class, OidcUserInfoMixin.class);
|
||||
context.setMixIn(OidcUserAuthority.class, OidcUserAuthorityMixin.class);
|
||||
context.setMixIn(DefaultOidcUser.class, DefaultOidcUserMixin.class);
|
||||
context.setMixIn(OAuth2AuthenticationToken.class, OAuth2AuthenticationTokenMixin.class);
|
||||
context.setMixIn(OAuth2AuthenticationException.class, OAuth2AuthenticationExceptionMixin.class);
|
||||
context.setMixIn(OAuth2Error.class, OAuth2ErrorMixin.class);
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2Error} as part of
|
||||
* {@link org.springframework.security.oauth2.core.OAuth2AuthenticationException}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Dennis Neufeld
|
||||
* @since 7.0
|
||||
* @see OAuth2Error
|
||||
* @see OAuth2AuthenticationExceptionMixin
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2ErrorMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2ErrorMixin(@JsonProperty("errorCode") String errorCode, @JsonProperty("description") String description,
|
||||
@JsonProperty("uri") String uri) {
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2RefreshToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2RefreshToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2RefreshTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2RefreshTokenMixin(@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt) {
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2UserAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2UserAuthority
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2UserAuthorityMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2UserAuthorityMixin(@JsonProperty("authority") String authority,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes) {
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcIdToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcIdToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OidcIdTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcIdTokenMixin(@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt,
|
||||
@JsonProperty("expiresAt") Instant expiresAt, @JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
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.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcUserAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcUserAuthority
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "attributes" })
|
||||
abstract class OidcUserAuthorityMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcUserAuthorityMixin(@JsonProperty("authority") String authority, @JsonProperty("idToken") OidcIdToken idToken,
|
||||
@JsonProperty("userInfo") OidcUserInfo userInfo) {
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcUserInfo}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcUserInfo
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OidcUserInfoMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcUserInfoMixin(@JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* {@code StdConverter} implementations.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class StdConverters {
|
||||
|
||||
static final class AccessTokenTypeConverter extends StdConverter<JsonNode, OAuth2AccessToken.TokenType> {
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken.TokenType convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(value)) {
|
||||
return OAuth2AccessToken.TokenType.BEARER;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class ClientAuthenticationMethodConverter extends StdConverter<JsonNode, ClientAuthenticationMethod> {
|
||||
|
||||
@Override
|
||||
public ClientAuthenticationMethod convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
return ClientAuthenticationMethod.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class AuthorizationGrantTypeConverter extends StdConverter<JsonNode, AuthorizationGrantType> {
|
||||
|
||||
@Override
|
||||
public AuthorizationGrantType convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
}
|
||||
if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.CLIENT_CREDENTIALS;
|
||||
}
|
||||
return new AuthorizationGrantType(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class AuthenticationMethodConverter extends StdConverter<JsonNode, AuthenticationMethod> {
|
||||
|
||||
@Override
|
||||
public AuthenticationMethod convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.HEADER;
|
||||
}
|
||||
if (AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.FORM;
|
||||
}
|
||||
if (AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.QUERY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support for OAuth2 client.
|
||||
*/
|
||||
package org.springframework.security.oauth2.client.jackson;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 2 serialization support for OAuth2 client.
|
||||
*/
|
||||
package org.springframework.security.oauth2.client.jackson2;
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationExceptionMixin}.
|
||||
*
|
||||
* @author Dennis Neufeld
|
||||
* @since 5.3.4
|
||||
*/
|
||||
public class OAuth2AuthenticationExceptionMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]", "Authorization Request Not Found", "/foo/bar"),
|
||||
"Authorization Request Not Found");
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
String expected = asJson(exception);
|
||||
JSONAssert.assertEquals(expected, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]"));
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
String expected = asJson(exception);
|
||||
JSONAssert.assertEquals(expected, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
String json = asJson(new OAuth2AuthenticationException(new OAuth2Error("[authorization_request_not_found]")));
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
OAuth2AuthenticationException expected = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]", "Authorization Request Not Found", "/foo/bar"),
|
||||
"Authorization Request Not Found");
|
||||
OAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),
|
||||
OAuth2AuthenticationException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isEqualTo(expected.getMessage());
|
||||
OAuth2Error oauth2Error = exception.getError();
|
||||
assertThat(oauth2Error).isNotNull();
|
||||
assertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());
|
||||
assertThat(oauth2Error.getDescription()).isEqualTo(expected.getError().getDescription());
|
||||
assertThat(oauth2Error.getUri()).isEqualTo(expected.getError().getUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
OAuth2AuthenticationException expected = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]"));
|
||||
OAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),
|
||||
OAuth2AuthenticationException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isNull();
|
||||
OAuth2Error oauth2Error = exception.getError();
|
||||
assertThat(oauth2Error).isNotNull();
|
||||
assertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());
|
||||
assertThat(oauth2Error.getDescription()).isNull();
|
||||
assertThat(oauth2Error.getUri()).isNull();
|
||||
}
|
||||
|
||||
private String asJson(OAuth2AuthenticationException exception) {
|
||||
OAuth2Error error = exception.getError();
|
||||
// @formatter:off
|
||||
return "\n{"
|
||||
+ "\n \"@class\": \"org.springframework.security.oauth2.core.OAuth2AuthenticationException\","
|
||||
+ "\n \"error\":"
|
||||
+ "\n {"
|
||||
+ "\n \"@class\":\"org.springframework.security.oauth2.core.OAuth2Error\","
|
||||
+ "\n \"errorCode\":\"" + error.getErrorCode() + "\","
|
||||
+ "\n \"description\":" + jsonStringOrNull(error.getDescription()) + ","
|
||||
+ "\n \"uri\":" + jsonStringOrNull(error.getUri())
|
||||
+ "\n },"
|
||||
+ "\n \"detailMessage\":" + jsonStringOrNull(exception.getMessage())
|
||||
+ "\n}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private String jsonStringOrNull(String input) {
|
||||
return (input != null) ? "\"" + input + "\"" : "null";
|
||||
}
|
||||
|
||||
}
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.DeserializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.TestOAuth2AuthenticationTokens;
|
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthenticationTokenMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder()
|
||||
.addModules(SecurityJacksonModules.getModules(loader))
|
||||
// see https://github.com/FasterXML/jackson-databind/issues/3052 for details
|
||||
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
// OidcUser
|
||||
OAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String expectedJson = asJson(authentication);
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
// OAuth2User
|
||||
authentication = TestOAuth2AuthenticationTokens.authenticated();
|
||||
expectedJson = asJson(authentication);
|
||||
json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
DefaultOidcUser principal = TestOidcUsers.create();
|
||||
principal = new DefaultOidcUser(principal.getAuthorities(), principal.getIdToken());
|
||||
OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(),
|
||||
"registration-id");
|
||||
String expectedJson = asJson(authentication);
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
OAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String json = asJson(authentication);
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationToken.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
// OidcUser
|
||||
OAuth2AuthenticationToken expectedAuthentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String json = asJson(expectedAuthentication);
|
||||
OAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOidcUser expectedOidcUser = (DefaultOidcUser) expectedAuthentication.getPrincipal();
|
||||
DefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();
|
||||
assertThat(oidcUser.getAuthorities().containsAll(expectedOidcUser.getAuthorities())).isTrue();
|
||||
assertThat(oidcUser.getAttributes()).containsExactlyEntriesOf(expectedOidcUser.getAttributes());
|
||||
assertThat(oidcUser.getName()).isEqualTo(expectedOidcUser.getName());
|
||||
OidcIdToken expectedIdToken = expectedOidcUser.getIdToken();
|
||||
OidcIdToken idToken = oidcUser.getIdToken();
|
||||
assertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());
|
||||
assertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());
|
||||
assertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());
|
||||
assertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());
|
||||
OidcUserInfo expectedUserInfo = expectedOidcUser.getUserInfo();
|
||||
OidcUserInfo userInfo = oidcUser.getUserInfo();
|
||||
assertThat(userInfo.getClaims()).containsExactlyEntriesOf(expectedUserInfo.getClaims());
|
||||
// OAuth2User
|
||||
expectedAuthentication = TestOAuth2AuthenticationTokens.authenticated();
|
||||
json = asJson(expectedAuthentication);
|
||||
authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOAuth2User expectedOauth2User = (DefaultOAuth2User) expectedAuthentication.getPrincipal();
|
||||
DefaultOAuth2User oauth2User = (DefaultOAuth2User) authentication.getPrincipal();
|
||||
assertThat(oauth2User.getAuthorities().containsAll(expectedOauth2User.getAuthorities())).isTrue();
|
||||
assertThat(oauth2User.getAttributes()).containsExactlyEntriesOf(expectedOauth2User.getAttributes());
|
||||
assertThat(oauth2User.getName()).isEqualTo(expectedOauth2User.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
DefaultOidcUser expectedPrincipal = TestOidcUsers.create();
|
||||
expectedPrincipal = new DefaultOidcUser(expectedPrincipal.getAuthorities(), expectedPrincipal.getIdToken());
|
||||
OAuth2AuthenticationToken expectedAuthentication = new OAuth2AuthenticationToken(expectedPrincipal,
|
||||
Collections.emptyList(), "registration-id");
|
||||
String json = asJson(expectedAuthentication);
|
||||
OAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).isEmpty();
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
|
||||
assertThat(principal.getAuthorities().containsAll(expectedPrincipal.getAuthorities())).isTrue();
|
||||
assertThat(principal.getAttributes()).containsExactlyEntriesOf(expectedPrincipal.getAttributes());
|
||||
assertThat(principal.getName()).isEqualTo(expectedPrincipal.getName());
|
||||
OidcIdToken expectedIdToken = expectedPrincipal.getIdToken();
|
||||
OidcIdToken idToken = principal.getIdToken();
|
||||
assertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());
|
||||
assertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());
|
||||
assertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());
|
||||
assertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());
|
||||
assertThat(principal.getUserInfo()).isNull();
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthenticationToken authentication) {
|
||||
String principalJson = (authentication.getPrincipal() instanceof DefaultOidcUser)
|
||||
? asJson((DefaultOidcUser) authentication.getPrincipal())
|
||||
: asJson((DefaultOAuth2User) authentication.getPrincipal());
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\",\n" +
|
||||
" \"principal\": " + principalJson + ",\n" +
|
||||
" \"authorities\": " + asJson(authentication.getAuthorities(), "java.util.Collections$UnmodifiableRandomAccessList") + ",\n" +
|
||||
" \"authorizedClientRegistrationId\": \"" + authentication.getAuthorizedClientRegistrationId() + "\",\n" +
|
||||
" \"details\": null\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(DefaultOAuth2User oauth2User) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.user.DefaultOAuth2User\",\n" +
|
||||
" \"authorities\": " + asJson(oauth2User.getAuthorities(), "java.util.Collections$UnmodifiableSet") + ",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"username\": \"user\"\n" +
|
||||
" },\n" +
|
||||
" \"nameAttributeKey\": \"username\"\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(DefaultOidcUser oidcUser) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser\",\n" +
|
||||
" \"authorities\": " + asJson(oidcUser.getAuthorities(), "java.util.Collections$UnmodifiableSet") + ",\n" +
|
||||
" \"idToken\": " + asJson(oidcUser.getIdToken()) + ",\n" +
|
||||
" \"userInfo\": " + asJson(oidcUser.getUserInfo()) + ",\n" +
|
||||
" \"nameAttributeKey\": \"" + IdTokenClaimNames.SUB + "\"\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(Collection<? extends GrantedAuthority> authorities, String classTypeInfo) {
|
||||
OAuth2UserAuthority oauth2UserAuthority = null;
|
||||
OidcUserAuthority oidcUserAuthority = null;
|
||||
List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
if (authority instanceof OidcUserAuthority) {
|
||||
oidcUserAuthority = (OidcUserAuthority) authority;
|
||||
}
|
||||
else if (authority instanceof OAuth2UserAuthority) {
|
||||
oauth2UserAuthority = (OAuth2UserAuthority) authority;
|
||||
}
|
||||
else if (authority instanceof SimpleGrantedAuthority) {
|
||||
simpleAuthorities.add((SimpleGrantedAuthority) authority);
|
||||
}
|
||||
}
|
||||
String authoritiesJson = (oidcUserAuthority != null) ? asJson(oidcUserAuthority)
|
||||
: (oauth2UserAuthority != null) ? asJson(oauth2UserAuthority) : "";
|
||||
if (!simpleAuthorities.isEmpty()) {
|
||||
if (StringUtils.hasLength(authoritiesJson)) {
|
||||
authoritiesJson += ",";
|
||||
}
|
||||
authoritiesJson += asJson(simpleAuthorities);
|
||||
}
|
||||
// @formatter:off
|
||||
return "[\n" +
|
||||
" \"" + classTypeInfo + "\",\n" +
|
||||
" [" + authoritiesJson + "]\n" +
|
||||
" ]";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2UserAuthority oauth2UserAuthority) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\",\n" +
|
||||
" \"authority\": \"" + oauth2UserAuthority.getAuthority() + "\",\n" +
|
||||
" \"userNameAttributeName\": \"username\",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"username\": \"user\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcUserAuthority oidcUserAuthority) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\",\n" +
|
||||
" \"authority\": \"" + oidcUserAuthority.getAuthority() + "\",\n" +
|
||||
" \"userNameAttributeName\": \"" + oidcUserAuthority.getUserNameAttributeName() + "\",\n" +
|
||||
" \"idToken\": " + asJson(oidcUserAuthority.getIdToken()) + ",\n" +
|
||||
" \"userInfo\": " + asJson(oidcUserAuthority.getUserInfo()) + "\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(List<SimpleGrantedAuthority> simpleAuthorities) {
|
||||
// @formatter:off
|
||||
return simpleAuthorities.stream()
|
||||
.map((authority) -> "{\n" +
|
||||
" \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n" +
|
||||
" \"authority\": \"" + authority.getAuthority() + "\"\n" +
|
||||
" }")
|
||||
.collect(Collectors.joining(","));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcIdToken idToken) {
|
||||
String aud = "";
|
||||
if (!CollectionUtils.isEmpty(idToken.getAudience())) {
|
||||
aud = StringUtils.collectionToDelimitedString(idToken.getAudience(), ",", "\"", "\"");
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.OidcIdToken\",\n" +
|
||||
" \"tokenValue\": \"" + idToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(idToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(idToken.getExpiresAt()) + ",\n" +
|
||||
" \"claims\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"iat\": [\n" +
|
||||
" \"java.time.Instant\",\n" +
|
||||
" " + toString(idToken.getIssuedAt()) + "\n" +
|
||||
" ],\n" +
|
||||
" \"exp\": [\n" +
|
||||
" \"java.time.Instant\",\n" +
|
||||
" " + toString(idToken.getExpiresAt()) + "\n" +
|
||||
" ],\n" +
|
||||
" \"sub\": \"" + idToken.getSubject() + "\",\n" +
|
||||
" \"iss\": \"" + idToken.getIssuer() + "\",\n" +
|
||||
" \"aud\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + aud + "]\n" +
|
||||
" ],\n" +
|
||||
" \"azp\": \"" + idToken.getAuthorizedParty() + "\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcUserInfo userInfo) {
|
||||
if (userInfo == null) {
|
||||
return null;
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.OidcUserInfo\",\n" +
|
||||
" \"claims\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"sub\": \"" + userInfo.getSubject() + "\",\n" +
|
||||
" \"name\": \"" + userInfo.getClaim(StandardClaimNames.NAME) + "\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String toString(Instant instant) {
|
||||
if (instant == null) {
|
||||
return null;
|
||||
}
|
||||
return DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizationRequestMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthorizationRequestMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
private OAuth2AuthorizationRequest.Builder authorizationRequestBuilder;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
Map<String, Object> additionalParameters = new LinkedHashMap<>();
|
||||
additionalParameters.put("param1", "value1");
|
||||
additionalParameters.put("param2", "value2");
|
||||
// @formatter:off
|
||||
this.authorizationRequestBuilder = TestOAuth2AuthorizationRequests.request()
|
||||
.scope("read", "write")
|
||||
.additionalParameters(additionalParameters);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String expectedJson = asJson(authorizationRequest);
|
||||
String json = this.mapper.writeValueAsString(authorizationRequest);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
// @formatter:off
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder
|
||||
.scopes(null)
|
||||
.state(null)
|
||||
.additionalParameters(Map::clear)
|
||||
.attributes(Map::clear)
|
||||
.build();
|
||||
// @formatter:on
|
||||
String expectedJson = asJson(authorizationRequest);
|
||||
String json = this.mapper.writeValueAsString(authorizationRequest);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
String json = asJson(this.authorizationRequestBuilder.build());
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizationRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
OAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String json = asJson(expectedAuthorizationRequest);
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);
|
||||
assertThat(authorizationRequest.getAuthorizationUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());
|
||||
assertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());
|
||||
assertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());
|
||||
assertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());
|
||||
assertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());
|
||||
assertThat(authorizationRequest.getScopes()).isEqualTo(expectedAuthorizationRequest.getScopes());
|
||||
assertThat(authorizationRequest.getState()).isEqualTo(expectedAuthorizationRequest.getState());
|
||||
assertThat(authorizationRequest.getAdditionalParameters())
|
||||
.containsExactlyEntriesOf(expectedAuthorizationRequest.getAdditionalParameters());
|
||||
assertThat(authorizationRequest.getAuthorizationRequestUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());
|
||||
assertThat(authorizationRequest.getAttributes())
|
||||
.containsExactlyEntriesOf(expectedAuthorizationRequest.getAttributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
// @formatter:off
|
||||
OAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.scopes(null)
|
||||
.state(null)
|
||||
.additionalParameters(Map::clear)
|
||||
.attributes(Map::clear)
|
||||
.build();
|
||||
// @formatter:on
|
||||
String json = asJson(expectedAuthorizationRequest);
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);
|
||||
assertThat(authorizationRequest.getAuthorizationUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());
|
||||
assertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());
|
||||
assertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());
|
||||
assertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());
|
||||
assertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());
|
||||
assertThat(authorizationRequest.getScopes()).isEmpty();
|
||||
assertThat(authorizationRequest.getState()).isNull();
|
||||
assertThat(authorizationRequest.getAdditionalParameters()).isEmpty();
|
||||
assertThat(authorizationRequest.getAuthorizationRequestUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());
|
||||
assertThat(authorizationRequest.getAttributes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenInvalidAuthorizationGrantTypeThenThrowJsonParseException() {
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String json = asJson(authorizationRequest).replace("authorization_code", "client_credentials");
|
||||
assertThatExceptionOfType(StreamReadException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(json, OAuth2AuthorizationRequest.class))
|
||||
.withMessageContaining("Invalid authorizationGrantType");
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthorizationRequest authorizationRequest) {
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(authorizationRequest.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String additionalParameters = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getAdditionalParameters())) {
|
||||
additionalParameters += "," + authorizationRequest.getAdditionalParameters()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + authorizationRequest.getAdditionalParameters().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
String attributes = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getAttributes())) {
|
||||
attributes += "," + authorizationRequest.getAttributes()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + authorizationRequest.getAttributes().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\",\n" +
|
||||
" \"authorizationUri\": \"" + authorizationRequest.getAuthorizationUri() + "\",\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + authorizationRequest.getGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"responseType\": {\n" +
|
||||
" \"value\": \"" + authorizationRequest.getResponseType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"clientId\": \"" + authorizationRequest.getClientId() + "\",\n" +
|
||||
" \"redirectUri\": \"" + authorizationRequest.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"state\": " + ((authorizationRequest.getState() != null) ? "\"" + authorizationRequest.getState() + "\"" : "null") + ",\n" +
|
||||
" \"additionalParameters\": {\n" +
|
||||
" " + additionalParameters + "\n" +
|
||||
" },\n" +
|
||||
" \"authorizationRequestUri\": \"" + authorizationRequest.getAuthorizationRequestUri() + "\",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" " + attributes + "\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.TestOAuth2AccessTokens;
|
||||
import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizedClientMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthorizedClientMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
private ClientRegistration.Builder clientRegistrationBuilder;
|
||||
|
||||
private OAuth2AccessToken accessToken;
|
||||
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
|
||||
private String principalName;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
Map<String, Object> providerConfigurationMetadata = new LinkedHashMap<>();
|
||||
providerConfigurationMetadata.put("config1", "value1");
|
||||
providerConfigurationMetadata.put("config2", "value2");
|
||||
// @formatter:off
|
||||
this.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()
|
||||
.authorizationGrantType(new AuthorizationGrantType("custom-grant"))
|
||||
.scope("read", "write")
|
||||
.providerConfigurationMetadata(providerConfigurationMetadata);
|
||||
// @formatter:on
|
||||
this.accessToken = TestOAuth2AccessTokens.scopes("read", "write");
|
||||
this.refreshToken = TestOAuth2RefreshTokens.refreshToken();
|
||||
this.principalName = "principal-name";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),
|
||||
this.principalName, this.accessToken, this.refreshToken);
|
||||
String expectedJson = asJson(authorizedClient);
|
||||
String json = this.mapper.writeValueAsString(authorizedClient);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()
|
||||
.clientSecret(null)
|
||||
.clientName(null)
|
||||
.userInfoUri(null)
|
||||
.userNameAttributeName(null)
|
||||
.jwkSetUri(null)
|
||||
.issuerUri(null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, this.principalName,
|
||||
TestOAuth2AccessTokens.noScopes());
|
||||
String expectedJson = asJson(authorizedClient);
|
||||
String json = this.mapper.writeValueAsString(authorizedClient);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),
|
||||
this.principalName, this.accessToken);
|
||||
String json = asJson(authorizedClient);
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizedClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
ClientRegistration expectedClientRegistration = this.clientRegistrationBuilder.build();
|
||||
OAuth2AccessToken expectedAccessToken = this.accessToken;
|
||||
OAuth2RefreshToken expectedRefreshToken = this.refreshToken;
|
||||
OAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,
|
||||
this.principalName, expectedAccessToken, expectedRefreshToken);
|
||||
String json = asJson(expectedAuthorizedClient);
|
||||
OAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);
|
||||
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
||||
assertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());
|
||||
assertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());
|
||||
assertThat(clientRegistration.getClientSecret()).isEqualTo(expectedClientRegistration.getClientSecret());
|
||||
assertThat(clientRegistration.getClientAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());
|
||||
assertThat(clientRegistration.getAuthorizationGrantType())
|
||||
.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());
|
||||
assertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());
|
||||
assertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());
|
||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getTokenUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(
|
||||
expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());
|
||||
assertThat(clientRegistration.getProviderDetails().getJwkSetUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getJwkSetUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getIssuerUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())
|
||||
.containsExactlyEntriesOf(clientRegistration.getProviderDetails().getConfigurationMetadata());
|
||||
assertThat(clientRegistration.getClientName()).isEqualTo(expectedClientRegistration.getClientName());
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
assertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());
|
||||
assertThat(accessToken.getScopes()).isEqualTo(expectedAccessToken.getScopes());
|
||||
assertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());
|
||||
assertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());
|
||||
assertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());
|
||||
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();
|
||||
assertThat(refreshToken.getTokenValue()).isEqualTo(expectedRefreshToken.getTokenValue());
|
||||
assertThat(refreshToken.getIssuedAt()).isEqualTo(expectedRefreshToken.getIssuedAt());
|
||||
assertThat(refreshToken.getExpiresAt()).isEqualTo(expectedRefreshToken.getExpiresAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
// @formatter:off
|
||||
ClientRegistration expectedClientRegistration = TestClientRegistrations.clientRegistration()
|
||||
.clientSecret(null)
|
||||
.clientName(null)
|
||||
.userInfoUri(null)
|
||||
.userNameAttributeName(null)
|
||||
.jwkSetUri(null)
|
||||
.issuerUri(null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
OAuth2AccessToken expectedAccessToken = TestOAuth2AccessTokens.noScopes();
|
||||
OAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,
|
||||
this.principalName, expectedAccessToken);
|
||||
String json = asJson(expectedAuthorizedClient);
|
||||
OAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);
|
||||
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
||||
assertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());
|
||||
assertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());
|
||||
assertThat(clientRegistration.getClientSecret()).isEmpty();
|
||||
assertThat(clientRegistration.getClientAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());
|
||||
assertThat(clientRegistration.getAuthorizationGrantType())
|
||||
.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());
|
||||
assertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());
|
||||
assertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());
|
||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getTokenUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getJwkSetUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();
|
||||
assertThat(clientRegistration.getClientName()).isEqualTo(clientRegistration.getRegistrationId());
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
assertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());
|
||||
assertThat(accessToken.getScopes()).isEmpty();
|
||||
assertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());
|
||||
assertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());
|
||||
assertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());
|
||||
assertThat(authorizedClient.getRefreshToken()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deserializeWhenClientSettingsPropertyDoesNotExistThenDefaulted() throws JacksonException {
|
||||
// ClientRegistration.clientSettings was added later, so old values will be
|
||||
// serialized without that property
|
||||
// this test checks for passivity
|
||||
ClientRegistration clientRegistration = this.clientRegistrationBuilder.build();
|
||||
ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();
|
||||
ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String configurationMetadata = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {
|
||||
configurationMetadata += "," + providerDetails.getConfigurationMetadata()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + providerDetails.getConfigurationMetadata().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
String json = "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n" +
|
||||
" \"registrationId\": \"" + clientRegistration.getRegistrationId() + "\",\n" +
|
||||
" \"clientId\": \"" + clientRegistration.getClientId() + "\",\n" +
|
||||
" \"clientSecret\": \"" + clientRegistration.getClientSecret() + "\",\n" +
|
||||
" \"clientAuthenticationMethod\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getClientAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getAuthorizationGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"redirectUri\": \"" + clientRegistration.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"providerDetails\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\",\n" +
|
||||
" \"authorizationUri\": \"" + providerDetails.getAuthorizationUri() + "\",\n" +
|
||||
" \"tokenUri\": \"" + providerDetails.getTokenUri() + "\",\n" +
|
||||
" \"userInfoEndpoint\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\",\n" +
|
||||
" \"uri\": " + ((userInfoEndpoint.getUri() != null) ? "\"" + userInfoEndpoint.getUri() + "\"" : null) + ",\n" +
|
||||
" \"authenticationMethod\": {\n" +
|
||||
" \"value\": \"" + userInfoEndpoint.getAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"userNameAttributeName\": " + ((userInfoEndpoint.getUserNameAttributeName() != null) ? "\"" + userInfoEndpoint.getUserNameAttributeName() + "\"" : null) + "\n" +
|
||||
" },\n" +
|
||||
" \"jwkSetUri\": " + ((providerDetails.getJwkSetUri() != null) ? "\"" + providerDetails.getJwkSetUri() + "\"" : null) + ",\n" +
|
||||
" \"issuerUri\": " + ((providerDetails.getIssuerUri() != null) ? "\"" + providerDetails.getIssuerUri() + "\"" : null) + ",\n" +
|
||||
" \"configurationMetadata\": {\n" +
|
||||
" " + configurationMetadata + "\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"clientName\": \"" + clientRegistration.getClientName() + "\"\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
// validate the test input
|
||||
assertThat(json).doesNotContain("clientSettings");
|
||||
ClientRegistration registration = this.mapper.readValue(json, ClientRegistration.class);
|
||||
// the default value of requireProofKey is false
|
||||
assertThat(registration.getClientSettings().isRequireProofKey()).isFalse();
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthorizedClient authorizedClient) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\",\n" +
|
||||
" \"clientRegistration\": " + asJson(authorizedClient.getClientRegistration()) + ",\n" +
|
||||
" \"principalName\": \"" + authorizedClient.getPrincipalName() + "\",\n" +
|
||||
" \"accessToken\": " + asJson(authorizedClient.getAccessToken()) + ",\n" +
|
||||
" \"refreshToken\": " + asJson(authorizedClient.getRefreshToken()) + "\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(ClientRegistration clientRegistration) {
|
||||
ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();
|
||||
ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String configurationMetadata = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {
|
||||
configurationMetadata += "," + providerDetails.getConfigurationMetadata()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + providerDetails.getConfigurationMetadata().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n" +
|
||||
" \"registrationId\": \"" + clientRegistration.getRegistrationId() + "\",\n" +
|
||||
" \"clientId\": \"" + clientRegistration.getClientId() + "\",\n" +
|
||||
" \"clientSecret\": \"" + clientRegistration.getClientSecret() + "\",\n" +
|
||||
" \"clientAuthenticationMethod\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getClientAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getAuthorizationGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"redirectUri\": \"" + clientRegistration.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"providerDetails\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\",\n" +
|
||||
" \"authorizationUri\": \"" + providerDetails.getAuthorizationUri() + "\",\n" +
|
||||
" \"tokenUri\": \"" + providerDetails.getTokenUri() + "\",\n" +
|
||||
" \"userInfoEndpoint\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\",\n" +
|
||||
" \"uri\": " + ((userInfoEndpoint.getUri() != null) ? "\"" + userInfoEndpoint.getUri() + "\"" : null) + ",\n" +
|
||||
" \"authenticationMethod\": {\n" +
|
||||
" \"value\": \"" + userInfoEndpoint.getAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"userNameAttributeName\": " + ((userInfoEndpoint.getUserNameAttributeName() != null) ? "\"" + userInfoEndpoint.getUserNameAttributeName() + "\"" : null) + "\n" +
|
||||
" },\n" +
|
||||
" \"jwkSetUri\": " + ((providerDetails.getJwkSetUri() != null) ? "\"" + providerDetails.getJwkSetUri() + "\"" : null) + ",\n" +
|
||||
" \"issuerUri\": " + ((providerDetails.getIssuerUri() != null) ? "\"" + providerDetails.getIssuerUri() + "\"" : null) + ",\n" +
|
||||
" \"configurationMetadata\": {\n" +
|
||||
" " + configurationMetadata + "\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"clientName\": \"" + clientRegistration.getClientName() + "\",\n" +
|
||||
" \"clientSettings\": {\n" +
|
||||
" \"requireProofKey\": " + clientRegistration.getClientSettings().isRequireProofKey() + "\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AccessToken accessToken) {
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(accessToken.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.OAuth2AccessToken\",\n" +
|
||||
" \"tokenType\": {\n" +
|
||||
" \"value\": \"" + accessToken.getTokenType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"tokenValue\": \"" + accessToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(accessToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(accessToken.getExpiresAt()) + ",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2RefreshToken refreshToken) {
|
||||
if (refreshToken == null) {
|
||||
return null;
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.OAuth2RefreshToken\",\n" +
|
||||
" \"tokenValue\": \"" + refreshToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(refreshToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(refreshToken.getExpiresAt()) + "\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String toString(Instant instant) {
|
||||
if (instant == null) {
|
||||
return null;
|
||||
}
|
||||
return DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.node.JsonNodeFactory;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class StdConvertersTests {
|
||||
|
||||
private final StdConverter<JsonNode, ClientAuthenticationMethod> clientAuthenticationMethodConverter = new org.springframework.security.oauth2.client.jackson.StdConverters.ClientAuthenticationMethodConverter();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("convertWhenClientAuthenticationMethodConvertedThenDeserializes")
|
||||
void convertWhenClientAuthenticationMethodConvertedThenDeserializes(String clientAuthenticationMethod) {
|
||||
ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
|
||||
jsonNode.put("value", clientAuthenticationMethod);
|
||||
ClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode);
|
||||
assertThat(actual.getValue()).isEqualTo(clientAuthenticationMethod);
|
||||
}
|
||||
|
||||
static Stream<Arguments> convertWhenClientAuthenticationMethodConvertedThenDeserializes() {
|
||||
return Stream.of(Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.NONE.getValue()), Arguments.of("custom_method"));
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -20,8 +20,6 @@ import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
@@ -29,6 +27,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -111,7 +111,7 @@ public class ClientRegistrationsTests {
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
private JsonMapper mapper = new JsonMapper();
|
||||
|
||||
private Map<String, Object> response;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ dependencies {
|
||||
testImplementation "jakarta.servlet:jakarta.servlet-api"
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver'
|
||||
testImplementation 'io.projectreactor.netty:reactor-netty'
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
testImplementation 'tools.jackson.core:jackson-databind'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
|
||||
+7
-12
@@ -21,10 +21,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
@@ -33,6 +29,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -191,8 +189,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponse(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -203,8 +200,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -216,8 +212,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -384,8 +379,8 @@ public class JwtDecodersTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public String buildResponseWithMissingJwksUri() throws JsonMappingException, JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
public String buildResponseWithMissingJwksUri() {
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
Map<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
||||
+6
-10
@@ -21,10 +21,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
@@ -33,6 +29,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -153,8 +151,7 @@ public class ReactiveJwtDecoderProviderConfigurationUtilsTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -165,8 +162,7 @@ public class ReactiveJwtDecoderProviderConfigurationUtilsTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -323,8 +319,8 @@ public class ReactiveJwtDecoderProviderConfigurationUtilsTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public String buildResponseWithMissingJwksUri() throws JsonMappingException, JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
public String buildResponseWithMissingJwksUri() {
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
Map<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
||||
+7
-12
@@ -21,10 +21,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
@@ -33,6 +29,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -166,8 +164,7 @@ public class ReactiveJwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponse(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -178,8 +175,7 @@ public class ReactiveJwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -190,8 +186,7 @@ public class ReactiveJwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -357,8 +352,8 @@ public class ReactiveJwtDecodersTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public String buildResponseWithMissingJwksUri() throws JsonMappingException, JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
public String buildResponseWithMissingJwksUri() {
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
Map<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ dependencies {
|
||||
testImplementation project(path : ':spring-security-core', configuration : 'tests')
|
||||
testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver'
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
testImplementation 'tools.jackson.core:jackson-databind'
|
||||
testImplementation 'io.projectreactor.netty:reactor-netty'
|
||||
testImplementation 'io.projectreactor:reactor-test'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
|
||||
+1
@@ -156,6 +156,7 @@ public final class OAuth2ProtectedResourceMetadataFilter extends OncePerRequestF
|
||||
private HttpMessageConverters() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
|
||||
if (jackson2Present) {
|
||||
return new MappingJackson2HttpMessageConverter();
|
||||
|
||||
+2
-2
@@ -26,13 +26,13 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -104,7 +104,7 @@ public class SpringReactiveOpaqueTokenIntrospectorTests {
|
||||
+ " }";
|
||||
// @formatter:on
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final JsonMapper mapper = new JsonMapper();
|
||||
|
||||
@Test
|
||||
public void authenticateWhenActiveTokenThenOk() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user