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
+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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user