Add Jackson support for oauth2-client session related classes

Fixes gh-4886
This commit is contained in:
Joe Grandja 2020-01-26 07:58:24 -05:00
parent ca5cc13948
commit 04f3fe8af9
28 changed files with 2044 additions and 33 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2020 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.
@ -61,6 +61,7 @@ import java.util.Set;
* mapper.registerModule(new WebJackson2Module());
* mapper.registerModule(new WebServletJackson2Module());
* mapper.registerModule(new WebServerJackson2Module());
* mapper.registerModule(new OAuth2ClientJackson2Module());
* </pre>
*
* @author Jitendra Singh.
@ -77,6 +78,10 @@ public final class SecurityJackson2Modules {
);
private static final String webServletJackson2ModuleClass =
"org.springframework.security.web.jackson2.WebServletJackson2Module";
private static final String oauth2ClientJackson2ModuleClass =
"org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module";
private static final String javaTimeJackson2ModuleClass =
"com.fasterxml.jackson.datatype.jsr310.JavaTimeModule";
private SecurityJackson2Modules() {
}
@ -121,6 +126,12 @@ public final class SecurityJackson2Modules {
if (ClassUtils.isPresent("javax.servlet.http.Cookie", loader)) {
addToModulesList(loader, modules, webServletJackson2ModuleClass);
}
if (ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient", loader)) {
addToModulesList(loader, modules, oauth2ClientJackson2ModuleClass);
}
if (ClassUtils.isPresent(javaTimeJackson2ModuleClass, loader)) {
addToModulesList(loader, modules, javaTimeJackson2ModuleClass);
}
return modules;
}
@ -188,8 +199,11 @@ public final class SecurityJackson2Modules {
"java.util.Collections$UnmodifiableRandomAccessList",
"java.util.Collections$SingletonList",
"java.util.Date",
"java.time.Instant",
"java.net.URL",
"java.util.TreeMap",
"java.util.HashMap",
"java.util.LinkedHashMap",
"org.springframework.security.core.context.SecurityContextImpl"
)));

View File

@ -28,6 +28,7 @@ dependencies {
constraints {
management "ch.qos.logback:logback-classic:1.+"
management "com.fasterxml.jackson.core:jackson-databind:2.+"
management 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.+'
management "com.google.appengine:appengine-api-1.0-sdk:$gaeVersion"
management "com.google.appengine:appengine-api-labs:$gaeVersion"
management "com.google.appengine:appengine-api-stubs:$gaeVersion"

View File

@ -10,15 +10,17 @@ dependencies {
optional project(':spring-security-oauth2-jose')
optional 'io.projectreactor:reactor-core'
optional 'org.springframework:spring-webflux'
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
testCompile project(path: ':spring-security-oauth2-core', configuration: 'tests')
testCompile project(path: ':spring-security-oauth2-jose', configuration: 'tests')
testCompile powerMock2Dependencies
testCompile 'com.squareup.okhttp3:mockwebserver'
testCompile 'com.fasterxml.jackson.core:jackson-databind'
testCompile 'io.projectreactor.netty:reactor-netty'
testCompile 'io.projectreactor:reactor-test'
testCompile 'io.projectreactor.tools:blockhound'
testCompile 'org.skyscreamer:jsonassert'
provided 'javax.servlet:javax.servlet-api'
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.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;
import java.io.IOException;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.MAP_TYPE_REFERENCE;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.SET_TYPE_REFERENCE;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findObjectNode;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findStringValue;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findValue;
/**
* A {@code JsonDeserializer} for {@link ClientRegistration}.
*
* @author Joe Grandja
* @since 5.3
* @see ClientRegistration
* @see ClientRegistrationMixin
*/
final class ClientRegistrationDeserializer extends JsonDeserializer<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) throws IOException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JsonNode clientRegistrationNode = mapper.readTree(parser);
JsonNode providerDetailsNode = findObjectNode(clientRegistrationNode, "providerDetails");
JsonNode userInfoEndpointNode = findObjectNode(providerDetailsNode, "userInfoEndpoint");
return ClientRegistration
.withRegistrationId(findStringValue(clientRegistrationNode, "registrationId"))
.clientId(findStringValue(clientRegistrationNode, "clientId"))
.clientSecret(findStringValue(clientRegistrationNode, "clientSecret"))
.clientAuthenticationMethod(
CLIENT_AUTHENTICATION_METHOD_CONVERTER.convert(
findObjectNode(clientRegistrationNode, "clientAuthenticationMethod")))
.authorizationGrantType(
AUTHORIZATION_GRANT_TYPE_CONVERTER.convert(
findObjectNode(clientRegistrationNode, "authorizationGrantType")))
.redirectUriTemplate(findStringValue(clientRegistrationNode, "redirectUriTemplate"))
.scope(findValue(clientRegistrationNode, "scopes", SET_TYPE_REFERENCE, mapper))
.clientName(findStringValue(clientRegistrationNode, "clientName"))
.authorizationUri(findStringValue(providerDetailsNode, "authorizationUri"))
.tokenUri(findStringValue(providerDetailsNode, "tokenUri"))
.userInfoUri(findStringValue(userInfoEndpointNode, "uri"))
.userInfoAuthenticationMethod(
AUTHENTICATION_METHOD_CONVERTER.convert(
findObjectNode(userInfoEndpointNode, "authenticationMethod")))
.userNameAttributeName(findStringValue(userInfoEndpointNode, "userNameAttributeName"))
.jwkSetUri(findStringValue(providerDetailsNode, "jwkSetUri"))
.providerConfigurationMetadata(findValue(providerDetailsNode, "configurationMetadata", MAP_TYPE_REFERENCE, mapper))
.build();
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.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 Joe Grandja
* @since 5.3
* @see ClientRegistration
* @see ClientRegistrationDeserializer
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = ClientRegistrationDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class ClientRegistrationMixin {
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import java.util.Collection;
import java.util.Map;
/**
* This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.
*
* @author Joe Grandja
* @since 5.3
* @see DefaultOAuth2User
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class DefaultOAuth2UserMixin {
@JsonCreator
DefaultOAuth2UserMixin(
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
@JsonProperty("attributes") Map<String, Object> attributes,
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.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;
import java.util.Collection;
/**
* This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.
*
* @author Joe Grandja
* @since 5.3
* @see DefaultOidcUser
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(value = {"attributes"}, ignoreUnknown = true)
abstract class DefaultOidcUserMixin {
@JsonCreator
DefaultOidcUserMixin(
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
@JsonProperty("idToken") OidcIdToken idToken,
@JsonProperty("userInfo") OidcUserInfo userInfo,
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import java.util.Set;
/**
* Utility class for {@code JsonNode}.
*
* @author Joe Grandja
* @since 5.3
*/
abstract class JsonNodeUtils {
static final TypeReference<Set<String>> SET_TYPE_REFERENCE = new TypeReference<Set<String>>() {};
static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() {};
static String findStringValue(JsonNode jsonNode, String fieldName) {
if (jsonNode == null) {
return null;
}
JsonNode nodeValue = jsonNode.findValue(fieldName);
if (nodeValue != null && nodeValue.isTextual()) {
return nodeValue.asText();
}
return null;
}
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference, ObjectMapper mapper) {
if (jsonNode == null) {
return null;
}
JsonNode nodeValue = jsonNode.findValue(fieldName);
if (nodeValue != null && nodeValue.isContainerNode()) {
return (T) mapper.convertValue(nodeValue, valueTypeReference);
}
return null;
}
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
if (jsonNode == null) {
return null;
}
JsonNode nodeValue = jsonNode.findValue(fieldName);
if (nodeValue != null && nodeValue.isObject()) {
return nodeValue;
}
return null;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import java.time.Instant;
import java.util.Set;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2AccessToken}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2AccessToken
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OAuth2AccessTokenMixin {
@JsonCreator
OAuth2AccessTokenMixin(
@JsonProperty("tokenType") @JsonDeserialize(converter = StdConverters.AccessTokenTypeConverter.class) OAuth2AccessToken.TokenType tokenType,
@JsonProperty("tokenValue") String tokenValue,
@JsonProperty("issuedAt") @JsonFormat(pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601, timezone = "UTC") Instant issuedAt,
@JsonProperty("expiresAt") @JsonFormat(pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601, timezone = "UTC") Instant expiresAt,
@JsonProperty("scopes") Set<String> scopes) {
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.Collection;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2AuthenticationToken}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2AuthenticationToken
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(value = {"authenticated"}, ignoreUnknown = true)
abstract class OAuth2AuthenticationTokenMixin {
@JsonCreator
OAuth2AuthenticationTokenMixin(
@JsonProperty("principal") OAuth2User principal,
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
@JsonProperty("authorizedClientRegistrationId") String authorizedClientRegistrationId) {
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.StdConverter;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import java.io.IOException;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.MAP_TYPE_REFERENCE;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.SET_TYPE_REFERENCE;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findObjectNode;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findStringValue;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findValue;
/**
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2AuthorizationRequest
* @see OAuth2AuthorizationRequestMixin
*/
final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAuth2AuthorizationRequest> {
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER =
new StdConverters.AuthorizationGrantTypeConverter();
@Override
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JsonNode authorizationRequestNode = mapper.readTree(parser);
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER.convert(
findObjectNode(authorizationRequestNode, "authorizationGrantType"));
OAuth2AuthorizationRequest.Builder builder;
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
builder = OAuth2AuthorizationRequest.authorizationCode();
} else if (AuthorizationGrantType.IMPLICIT.equals(authorizationGrantType)) {
builder = OAuth2AuthorizationRequest.implicit();
} else {
throw new JsonParseException(parser, "Invalid authorizationGrantType");
}
return builder
.authorizationUri(findStringValue(authorizationRequestNode, "authorizationUri"))
.clientId(findStringValue(authorizationRequestNode, "clientId"))
.redirectUri(findStringValue(authorizationRequestNode, "redirectUri"))
.scopes(findValue(authorizationRequestNode, "scopes", SET_TYPE_REFERENCE, mapper))
.state(findStringValue(authorizationRequestNode, "state"))
.additionalParameters(findValue(authorizationRequestNode, "additionalParameters", MAP_TYPE_REFERENCE, mapper))
.authorizationRequestUri(findStringValue(authorizationRequestNode, "authorizationRequestUri"))
.attributes(findValue(authorizationRequestNode, "attributes", MAP_TYPE_REFERENCE, mapper))
.build();
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2AuthorizationRequest
* @see OAuth2AuthorizationRequestDeserializer
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OAuth2AuthorizationRequestMixin {
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.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 Joe Grandja
* @since 5.3
* @see OAuth2AuthorizedClient
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OAuth2AuthorizedClientMixin {
@JsonCreator
OAuth2AuthorizedClientMixin(
@JsonProperty("clientRegistration") ClientRegistration clientRegistration,
@JsonProperty("principalName") String principalName,
@JsonProperty("accessToken") OAuth2AccessToken accessToken,
@JsonProperty("refreshToken") OAuth2RefreshToken refreshToken) {
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.security.jackson2.SecurityJackson2Modules;
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.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;
import java.util.Collections;
/**
* 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>
* </ul>
*
* If not already enabled, default typing will be automatically enabled
* as type info is required to properly serialize/deserialize objects.
* In order to use this module just add it to your {@code ObjectMapper} configuration.
*
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new OAuth2ClientJackson2Module());
* </pre>
*
* <b>NOTE:</b> Use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get a list of all security modules.
*
* @author Joe Grandja
* @since 5.3
* @see SecurityJackson2Modules
* @see OAuth2AuthorizationRequestMixin
* @see ClientRegistrationMixin
* @see OAuth2AccessTokenMixin
* @see OAuth2RefreshTokenMixin
* @see OAuth2AuthorizedClientMixin
* @see OAuth2UserAuthorityMixin
* @see DefaultOAuth2UserMixin
* @see OidcIdTokenMixin
* @see OidcUserInfoMixin
* @see OidcUserAuthorityMixin
* @see DefaultOidcUserMixin
* @see OAuth2AuthenticationTokenMixin
*/
public class OAuth2ClientJackson2Module extends SimpleModule {
public OAuth2ClientJackson2Module() {
super(OAuth2ClientJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
}
@Override
public void setupModule(SetupContext context) {
SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
context.setMixInAnnotations(Collections.unmodifiableMap(Collections.emptyMap()).getClass(), UnmodifiableMapMixin.class);
context.setMixInAnnotations(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
context.setMixInAnnotations(ClientRegistration.class, ClientRegistrationMixin.class);
context.setMixInAnnotations(OAuth2AccessToken.class, OAuth2AccessTokenMixin.class);
context.setMixInAnnotations(OAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);
context.setMixInAnnotations(OAuth2AuthorizedClient.class, OAuth2AuthorizedClientMixin.class);
context.setMixInAnnotations(OAuth2UserAuthority.class, OAuth2UserAuthorityMixin.class);
context.setMixInAnnotations(DefaultOAuth2User.class, DefaultOAuth2UserMixin.class);
context.setMixInAnnotations(OidcIdToken.class, OidcIdTokenMixin.class);
context.setMixInAnnotations(OidcUserInfo.class, OidcUserInfoMixin.class);
context.setMixInAnnotations(OidcUserAuthority.class, OidcUserAuthorityMixin.class);
context.setMixInAnnotations(DefaultOidcUser.class, DefaultOidcUserMixin.class);
context.setMixInAnnotations(OAuth2AuthenticationToken.class, OAuth2AuthenticationTokenMixin.class);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import java.time.Instant;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2RefreshToken}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2RefreshToken
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OAuth2RefreshTokenMixin {
@JsonCreator
OAuth2RefreshTokenMixin(
@JsonProperty("tokenValue") String tokenValue,
@JsonProperty("issuedAt") @JsonFormat(pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601, timezone = "UTC") Instant issuedAt) {
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import java.util.Map;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2UserAuthority}.
*
* @author Joe Grandja
* @since 5.3
* @see OAuth2UserAuthority
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OAuth2UserAuthorityMixin {
@JsonCreator
OAuth2UserAuthorityMixin(
@JsonProperty("authority") String authority,
@JsonProperty("attributes") Map<String, Object> attributes) {
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import java.time.Instant;
import java.util.Map;
/**
* This mixin class is used to serialize/deserialize {@link OidcIdToken}.
*
* @author Joe Grandja
* @since 5.3
* @see OidcIdToken
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OidcIdTokenMixin {
@JsonCreator
OidcIdTokenMixin(
@JsonProperty("tokenValue") String tokenValue,
@JsonProperty("issuedAt") @JsonFormat(pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601, timezone = "UTC") Instant issuedAt,
@JsonProperty("expiresAt") @JsonFormat(pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601, timezone = "UTC") Instant expiresAt,
@JsonProperty("claims") Map<String, Object> claims) {
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.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 Joe Grandja
* @since 5.3
* @see OidcUserAuthority
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(value = {"attributes"}, ignoreUnknown = true)
abstract class OidcUserAuthorityMixin {
@JsonCreator
OidcUserAuthorityMixin(
@JsonProperty("authority") String authority,
@JsonProperty("idToken") OidcIdToken idToken,
@JsonProperty("userInfo") OidcUserInfo userInfo) {
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import java.util.Map;
/**
* This mixin class is used to serialize/deserialize {@link OidcUserInfo}.
*
* @author Joe Grandja
* @since 5.3
* @see OidcUserInfo
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class OidcUserInfoMixin {
@JsonCreator
OidcUserInfoMixin(@JsonProperty("claims") Map<String, Object> claims) {
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.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;
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findStringValue;
/**
* {@code StdConverter} implementations.
*
* @author Joe Grandja
* @since 5.3
*/
abstract class StdConverters {
static final class AccessTokenTypeConverter extends StdConverter<JsonNode, OAuth2AccessToken.TokenType> {
@Override
public OAuth2AccessToken.TokenType convert(JsonNode jsonNode) {
String value = 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 = findStringValue(jsonNode, "value");
if (ClientAuthenticationMethod.BASIC.getValue().equalsIgnoreCase(value)) {
return ClientAuthenticationMethod.BASIC;
} else if (ClientAuthenticationMethod.POST.getValue().equalsIgnoreCase(value)) {
return ClientAuthenticationMethod.POST;
} else if (ClientAuthenticationMethod.NONE.getValue().equalsIgnoreCase(value)) {
return ClientAuthenticationMethod.NONE;
}
return null;
}
}
static final class AuthorizationGrantTypeConverter extends StdConverter<JsonNode, AuthorizationGrantType> {
@Override
public AuthorizationGrantType convert(JsonNode jsonNode) {
String value = findStringValue(jsonNode, "value");
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
return AuthorizationGrantType.AUTHORIZATION_CODE;
} else if (AuthorizationGrantType.IMPLICIT.getValue().equalsIgnoreCase(value)) {
return AuthorizationGrantType.IMPLICIT;
} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {
return AuthorizationGrantType.CLIENT_CREDENTIALS;
} else if (AuthorizationGrantType.PASSWORD.getValue().equalsIgnoreCase(value)) {
return AuthorizationGrantType.PASSWORD;
}
return null;
}
}
static final class AuthenticationMethodConverter extends StdConverter<JsonNode, AuthenticationMethod> {
@Override
public AuthenticationMethod convert(JsonNode jsonNode) {
String value = findStringValue(jsonNode, "value");
if (AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {
return AuthenticationMethod.HEADER;
} else if (AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {
return AuthenticationMethod.FORM;
} else if (AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {
return AuthenticationMethod.QUERY;
}
return null;
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A {@code JsonDeserializer} for {@link Collections#unmodifiableMap(Map)}.
*
* @author Joe Grandja
* @since 5.3
* @see Collections#unmodifiableMap(Map)
* @see UnmodifiableMapMixin
*/
final class UnmodifiableMapDeserializer extends JsonDeserializer<Map<?, ?>> {
@Override
public Map<?, ?> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JsonNode mapNode = mapper.readTree(parser);
Map<String, Object> result = new LinkedHashMap<>();
if (mapNode != null && mapNode.isObject()) {
Iterable<Map.Entry<String, JsonNode>> fields = mapNode::fields;
for (Map.Entry<String, JsonNode> field : fields) {
result.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class));
}
}
return Collections.unmodifiableMap(result);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Collections;
import java.util.Map;
/**
* This mixin class is used to serialize/deserialize {@link Collections#unmodifiableMap(Map)}.
* It also registers a custom deserializer {@link UnmodifiableMapDeserializer}.
*
* @author Joe Grandja
* @since 5.3
* @see Collections#unmodifiableMap(Map)
* @see UnmodifiableMapDeserializer
* @see OAuth2ClientJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = UnmodifiableMapDeserializer.class)
abstract class UnmodifiableMapMixin {
@JsonCreator
UnmodifiableMapMixin(Map<?, ?> map) {
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@ -16,11 +16,9 @@
package org.springframework.security.oauth2.client.authentication;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.TestOAuth2Users;
/**
@ -28,10 +26,16 @@ import org.springframework.security.oauth2.core.user.TestOAuth2Users;
* @since 5.2
*/
public class TestOAuth2AuthenticationTokens {
public static OAuth2AuthenticationToken authenticated(String... roles) {
OAuth2User principal = TestOAuth2Users.create();
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(roles);
public static OAuth2AuthenticationToken authenticated() {
DefaultOAuth2User principal = TestOAuth2Users.create();
String registrationId = "registration-id";
return new OAuth2AuthenticationToken(principal, authorities, registrationId);
return new OAuth2AuthenticationToken(principal, principal.getAuthorities(), registrationId);
}
public static OAuth2AuthenticationToken oidcAuthenticated() {
DefaultOidcUser principal = TestOidcUsers.create();
String registrationId = "registration-id";
return new OAuth2AuthenticationToken(principal, principal.getAuthorities(), registrationId);
}
}

View File

@ -0,0 +1,351 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.junit.Before;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jackson2.SecurityJackson2Modules;
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.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 java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.security.oauth2.core.oidc.StandardClaimNames.NAME;
/**
* Tests for {@link OAuth2AuthenticationTokenMixin}.
*
* @author Joe Grandja
*/
public class OAuth2AuthenticationTokenMixinTests {
private static DateFormat dateFormatter;
private ObjectMapper mapper;
@Before
public void setup() {
ClassLoader loader = getClass().getClassLoader();
this.mapper = new ObjectMapper();
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
this.mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
dateFormatter = this.mapper.getDateFormat();
}
@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);
assertThatThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthenticationToken.class))
.isInstanceOf(JsonProcessingException.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.isEmpty(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" +
" \"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" +
" \"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\": \"" + dateFormatter.format(Date.from(idToken.getIssuedAt())) + "\",\n" +
" \"expiresAt\": \"" + dateFormatter.format(Date.from(idToken.getExpiresAt())) + "\",\n" +
" \"claims\": {\n" +
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
" \"iat\": [\n" +
" \"java.time.Instant\",\n" +
" \"" + idToken.getIssuedAt().toString() + "\"\n" +
" ],\n" +
" \"exp\": [\n" +
" \"java.time.Instant\",\n" +
" \"" + idToken.getExpiresAt().toString() + "\"\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(NAME) + "\"\n" +
" }\n" +
" }";
// @formatter:on
}
}

View File

@ -0,0 +1,197 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.security.jackson2.SecurityJackson2Modules;
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 java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2AuthorizationRequestMixin}.
*
* @author Joe Grandja
*/
public class OAuth2AuthorizationRequestMixinTests {
private ObjectMapper mapper;
private OAuth2AuthorizationRequest.Builder authorizationRequestBuilder;
@Before
public void setup() {
ClassLoader loader = getClass().getClassLoader();
this.mapper = new ObjectMapper();
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
Map<String, Object> additionalParameters = new LinkedHashMap<>();
additionalParameters.put("param1", "value1");
additionalParameters.put("param2", "value2");
this.authorizationRequestBuilder = TestOAuth2AuthorizationRequests.request()
.scope("read", "write")
.additionalParameters(additionalParameters);
}
@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 {
OAuth2AuthorizationRequest authorizationRequest =
this.authorizationRequestBuilder
.scopes(null)
.state(null)
.additionalParameters(null)
.attributes(null)
.build();
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());
assertThatThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthorizationRequest.class))
.isInstanceOf(JsonProcessingException.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 {
OAuth2AuthorizationRequest expectedAuthorizationRequest =
this.authorizationRequestBuilder
.scopes(null)
.state(null)
.additionalParameters(null)
.attributes(null)
.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()).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");
assertThatThrownBy(() -> this.mapper.readValue(json, OAuth2AuthorizationRequest.class))
.isInstanceOf(JsonParseException.class)
.hasMessageContaining("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
}
}

View File

@ -0,0 +1,325 @@
/*
* Copyright 2002-2020 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.jackson2;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.security.jackson2.SecurityJackson2Modules;
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.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 java.text.DateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2AuthorizedClientMixin}.
*
* @author Joe Grandja
*/
public class OAuth2AuthorizedClientMixinTests {
private static DateFormat dateFormatter;
private ObjectMapper mapper;
private ClientRegistration.Builder clientRegistrationBuilder;
private OAuth2AccessToken accessToken;
private OAuth2RefreshToken refreshToken;
private String principalName;
@Before
public void setup() {
ClassLoader loader = getClass().getClassLoader();
this.mapper = new ObjectMapper();
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
dateFormatter = this.mapper.getDateFormat();
Map<String, Object> providerConfigurationMetadata = new LinkedHashMap<>();
providerConfigurationMetadata.put("config1", "value1");
providerConfigurationMetadata.put("config2", "value2");
this.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()
.scope("read", "write")
.providerConfigurationMetadata(providerConfigurationMetadata);
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 {
ClientRegistration clientRegistration =
TestClientRegistrations.clientRegistration()
.clientSecret(null)
.clientName(null)
.userInfoUri(null)
.userNameAttributeName(null)
.jwkSetUri(null)
.build();
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);
assertThatThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthorizedClient.class))
.isInstanceOf(JsonProcessingException.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.getRedirectUriTemplate())
.isEqualTo(expectedClientRegistration.getRedirectUriTemplate());
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().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 {
ClientRegistration expectedClientRegistration =
TestClientRegistrations.clientRegistration()
.clientSecret(null)
.clientName(null)
.userInfoUri(null)
.userNameAttributeName(null)
.jwkSetUri(null)
.build();
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.getRedirectUriTemplate())
.isEqualTo(expectedClientRegistration.getRedirectUriTemplate());
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().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();
}
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" +
" \"redirectUriTemplate\": \"" + clientRegistration.getRedirectUriTemplate() + "\",\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" +
" \"configurationMetadata\": {\n" +
" " + configurationMetadata + "\n" +
" }\n" +
" },\n" +
" \"clientName\": \"" + clientRegistration.getClientName() + "\"\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\": \"" + dateFormatter.format(Date.from(accessToken.getIssuedAt())) + "\",\n" +
" \"expiresAt\": \"" + dateFormatter.format(Date.from(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\": \"" + dateFormatter.format(Date.from(refreshToken.getIssuedAt())) + "\",\n" +
" \"expiresAt\": " + (refreshToken.getExpiresAt() != null ? "\"" + dateFormatter.format(Date.from(refreshToken.getExpiresAt())) + "\"" : null) + "\n" +
"}";
// @formatter:on
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
@ -16,15 +16,15 @@
package org.springframework.security.oauth2.core.oidc.user;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashSet;
/**
* @author Joe Grandja
@ -32,16 +32,37 @@ import java.util.Map;
public class TestOidcUsers {
public static DefaultOidcUser create() {
List<GrantedAuthority> roles = AuthorityUtils.createAuthorityList("ROLE_USER");
return new DefaultOidcUser(roles, idToken());
OidcIdToken idToken = idToken();
OidcUserInfo userInfo = userInfo();
return new DefaultOidcUser(
authorities(idToken, userInfo), idToken, userInfo);
}
private static OidcIdToken idToken() {
Map<String, Object> claims = new HashMap<>();
claims.put(IdTokenClaimNames.SUB, "subject");
claims.put(IdTokenClaimNames.ISS, "http://localhost/issuer");
claims.put(IdTokenClaimNames.AUD, Collections.singletonList("client"));
claims.put(IdTokenClaimNames.AZP, "client");
return new OidcIdToken("id-token", Instant.now(), Instant.now().plusSeconds(3600), claims);
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plusSeconds(3600);
return OidcIdToken.withTokenValue("id-token")
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.subject("subject")
.issuer("http://localhost/issuer")
.audience(Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList("client"))))
.authorizedParty("client")
.build();
}
private static OidcUserInfo userInfo() {
return OidcUserInfo.builder()
.subject("subject")
.name("full name")
.build();
}
private static Collection<? extends GrantedAuthority> authorities(OidcIdToken idToken, OidcUserInfo userInfo) {
return new LinkedHashSet<>(
Arrays.asList(
new OidcUserAuthority(idToken, userInfo),
new SimpleGrantedAuthority("SCOPE_read"),
new SimpleGrantedAuthority("SCOPE_write")));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
@ -17,10 +17,12 @@
package org.springframework.security.oauth2.core.user;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Map;
/**
@ -29,10 +31,18 @@ import java.util.Map;
public class TestOAuth2Users {
public static DefaultOAuth2User create() {
List<GrantedAuthority> roles = AuthorityUtils.createAuthorityList("ROLE_USER");
String attrName = "username";
String nameAttributeKey = "username";
Map<String, Object> attributes = new HashMap<>();
attributes.put(attrName, "user");
return new DefaultOAuth2User(roles, attributes, attrName);
attributes.put(nameAttributeKey, "user");
Collection<GrantedAuthority> authorities = authorities(attributes);
return new DefaultOAuth2User(authorities, attributes, nameAttributeKey);
}
private static Collection<GrantedAuthority> authorities(Map<String, Object> attributes) {
return new LinkedHashSet<>(
Arrays.asList(
new OAuth2UserAuthority(attributes),
new SimpleGrantedAuthority("SCOPE_read"),
new SimpleGrantedAuthority("SCOPE_write")));
}
}