From 6470c71e772a9b4f7371574feca8bd5dc4b4c8b1 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Wed, 4 May 2022 05:36:31 -0400 Subject: [PATCH] Remove temporary OAuth2AccessTokenResponseHttpMessageConverter Issue gh-321 Closes gh-726 --- ...cessTokenResponseHttpMessageConverter.java | 376 ------------------ .../web/OAuth2TokenEndpointFilter.java | 3 +- .../web/OAuth2TokenEndpointFilterTests.java | 3 +- 3 files changed, 4 insertions(+), 378 deletions(-) delete mode 100644 oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AccessTokenResponseHttpMessageConverter.java diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AccessTokenResponseHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AccessTokenResponseHttpMessageConverter.java deleted file mode 100644 index 84b5ac5a..00000000 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AccessTokenResponseHttpMessageConverter.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright 2002-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.oauth2.server.authorization.web; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpInputMessage; -import org.springframework.http.HttpOutputMessage; -import org.springframework.http.MediaType; -import org.springframework.http.converter.AbstractHttpMessageConverter; -import org.springframework.http.converter.GenericHttpMessageConverter; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.HttpMessageNotWritableException; -import org.springframework.http.converter.json.GsonHttpMessageConverter; -import org.springframework.http.converter.json.JsonbHttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.endpoint.MapOAuth2AccessTokenResponseConverter; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponseMapConverter; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * TODO - * This class is a copy from Spring Security and should be removed after upgrading to Spring Security 5.6.0 GA. - * - * A {@link HttpMessageConverter} for an {@link OAuth2AccessTokenResponse OAuth 2.0 Access - * Token Response}. - * - * @author Joe Grandja - * @since 5.1 - * @see AbstractHttpMessageConverter - * @see OAuth2AccessTokenResponse - */ -class OAuth2AccessTokenResponseHttpMessageConverter - extends AbstractHttpMessageConverter { - - private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - - private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { - }; - - private GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); - - /** - * @deprecated This field should no longer be used - */ - @Deprecated - protected Converter, OAuth2AccessTokenResponse> tokenResponseConverter = new MapOAuth2AccessTokenResponseConverter(); - - private Converter, OAuth2AccessTokenResponse> accessTokenResponseConverter = new DefaultMapOAuth2AccessTokenResponseConverter(); - - /** - * @deprecated This field should no longer be used - */ - @Deprecated - protected Converter> tokenResponseParametersConverter = new OAuth2AccessTokenResponseMapConverter(); - - private Converter> accessTokenResponseParametersConverter = new DefaultOAuth2AccessTokenResponseMapConverter(); - - OAuth2AccessTokenResponseHttpMessageConverter() { - super(DEFAULT_CHARSET, MediaType.APPLICATION_JSON, new MediaType("application", "*+json")); - } - - @Override - protected boolean supports(Class clazz) { - return OAuth2AccessTokenResponse.class.isAssignableFrom(clazz); - } - - @Override - @SuppressWarnings("unchecked") - protected OAuth2AccessTokenResponse readInternal(Class clazz, - HttpInputMessage inputMessage) throws HttpMessageNotReadableException { - try { - Map tokenResponseParameters = (Map) this.jsonMessageConverter - .read(STRING_OBJECT_MAP.getType(), null, inputMessage); - // Only use deprecated converter if it has been set directly - if (this.tokenResponseConverter.getClass() != MapOAuth2AccessTokenResponseConverter.class) { - // gh-6463: Parse parameter values as Object in order to handle potential - // JSON Object and then convert values to String - Map stringTokenResponseParameters = new HashMap<>(); - tokenResponseParameters - .forEach((key, value) -> stringTokenResponseParameters.put(key, String.valueOf(value))); - return this.tokenResponseConverter.convert(stringTokenResponseParameters); - } - return this.accessTokenResponseConverter.convert(tokenResponseParameters); - } - catch (Exception ex) { - throw new HttpMessageNotReadableException( - "An error occurred reading the OAuth 2.0 Access Token Response: " + ex.getMessage(), ex, - inputMessage); - } - } - - @Override - protected void writeInternal(OAuth2AccessTokenResponse tokenResponse, HttpOutputMessage outputMessage) - throws HttpMessageNotWritableException { - try { - Map tokenResponseParameters; - // Only use deprecated converter if it has been set directly - if (this.tokenResponseParametersConverter.getClass() != OAuth2AccessTokenResponseMapConverter.class) { - tokenResponseParameters = new LinkedHashMap<>( - this.tokenResponseParametersConverter.convert(tokenResponse)); - } - else { - tokenResponseParameters = this.accessTokenResponseParametersConverter.convert(tokenResponse); - } - this.jsonMessageConverter.write(tokenResponseParameters, STRING_OBJECT_MAP.getType(), - MediaType.APPLICATION_JSON, outputMessage); - } - catch (Exception ex) { - throw new HttpMessageNotWritableException( - "An error occurred writing the OAuth 2.0 Access Token Response: " + ex.getMessage(), ex); - } - } - - /** - * Sets the {@link Converter} used for converting the OAuth 2.0 Access Token Response - * parameters to an {@link OAuth2AccessTokenResponse}. - * @deprecated Use {@link #setAccessTokenResponseConverter(Converter)} instead - * @param tokenResponseConverter the {@link Converter} used for converting to an - * {@link OAuth2AccessTokenResponse} - */ - @Deprecated - public final void setTokenResponseConverter( - Converter, OAuth2AccessTokenResponse> tokenResponseConverter) { - Assert.notNull(tokenResponseConverter, "tokenResponseConverter cannot be null"); - this.tokenResponseConverter = tokenResponseConverter; - } - - /** - * Sets the {@link Converter} used for converting the OAuth 2.0 Access Token Response - * parameters to an {@link OAuth2AccessTokenResponse}. - * @param accessTokenResponseConverter the {@link Converter} used for converting to an - * {@link OAuth2AccessTokenResponse} - * @since 5.6 - */ - public final void setAccessTokenResponseConverter( - Converter, OAuth2AccessTokenResponse> accessTokenResponseConverter) { - Assert.notNull(accessTokenResponseConverter, "accessTokenResponseConverter cannot be null"); - this.accessTokenResponseConverter = accessTokenResponseConverter; - } - - /** - * Sets the {@link Converter} used for converting the - * {@link OAuth2AccessTokenResponse} to a {@code Map} representation of the OAuth 2.0 - * Access Token Response parameters. - * @deprecated Use {@link #setAccessTokenResponseParametersConverter(Converter)} - * instead - * @param tokenResponseParametersConverter the {@link Converter} used for converting - * to a {@code Map} representation of the Access Token Response parameters - */ - @Deprecated - public final void setTokenResponseParametersConverter( - Converter> tokenResponseParametersConverter) { - Assert.notNull(tokenResponseParametersConverter, "tokenResponseParametersConverter cannot be null"); - this.tokenResponseParametersConverter = tokenResponseParametersConverter; - } - - /** - * Sets the {@link Converter} used for converting the - * {@link OAuth2AccessTokenResponse} to a {@code Map} representation of the OAuth 2.0 - * Access Token Response parameters. - * @param accessTokenResponseParametersConverter the {@link Converter} used for - * converting to a {@code Map} representation of the Access Token Response parameters - * @since 5.6 - */ - public final void setAccessTokenResponseParametersConverter( - Converter> accessTokenResponseParametersConverter) { - Assert.notNull(accessTokenResponseParametersConverter, "accessTokenResponseParametersConverter cannot be null"); - this.accessTokenResponseParametersConverter = accessTokenResponseParametersConverter; - } - - /** - * Utility methods for {@link HttpMessageConverter}'s. - * - * @author Joe Grandja - * @since 5.1 - */ - static final class HttpMessageConverters { - - private static final boolean jackson2Present; - - private static final boolean gsonPresent; - - private static final boolean jsonbPresent; - - static { - ClassLoader classLoader = HttpMessageConverters.class.getClassLoader(); - jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) - && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); - gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); - jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader); - } - - private HttpMessageConverters() { - } - - static GenericHttpMessageConverter getJsonMessageConverter() { - if (jackson2Present) { - return new MappingJackson2HttpMessageConverter(); - } - if (gsonPresent) { - return new GsonHttpMessageConverter(); - } - if (jsonbPresent) { - return new JsonbHttpMessageConverter(); - } - return null; - } - - } - - /** - * A {@link Converter} that converts the provided OAuth 2.0 Access Token Response - * parameters to an {@link OAuth2AccessTokenResponse}. - * - * @author Steve Riesenberg - * @since 5.6 - */ - static final class DefaultMapOAuth2AccessTokenResponseConverter - implements Converter, OAuth2AccessTokenResponse> { - - private static final Set TOKEN_RESPONSE_PARAMETER_NAMES = new HashSet<>( - Arrays.asList(OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.EXPIRES_IN, - OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ParameterNames.SCOPE, OAuth2ParameterNames.TOKEN_TYPE)); - - @Override - public OAuth2AccessTokenResponse convert(Map source) { - String accessToken = getParameterValue(source, OAuth2ParameterNames.ACCESS_TOKEN); - OAuth2AccessToken.TokenType accessTokenType = getAccessTokenType(source); - long expiresIn = getExpiresIn(source); - Set scopes = getScopes(source); - String refreshToken = getParameterValue(source, OAuth2ParameterNames.REFRESH_TOKEN); - Map additionalParameters = new LinkedHashMap<>(); - for (Map.Entry entry : source.entrySet()) { - if (!TOKEN_RESPONSE_PARAMETER_NAMES.contains(entry.getKey())) { - additionalParameters.put(entry.getKey(), entry.getValue()); - } - } - // @formatter:off - return OAuth2AccessTokenResponse.withToken(accessToken) - .tokenType(accessTokenType) - .expiresIn(expiresIn) - .scopes(scopes) - .refreshToken(refreshToken) - .additionalParameters(additionalParameters) - .build(); - // @formatter:on - } - - private static OAuth2AccessToken.TokenType getAccessTokenType(Map tokenResponseParameters) { - if (OAuth2AccessToken.TokenType.BEARER.getValue() - .equalsIgnoreCase(getParameterValue(tokenResponseParameters, OAuth2ParameterNames.TOKEN_TYPE))) { - return OAuth2AccessToken.TokenType.BEARER; - } - return null; - } - - private static long getExpiresIn(Map tokenResponseParameters) { - return getParameterValue(tokenResponseParameters, OAuth2ParameterNames.EXPIRES_IN, 0L); - } - - private static Set getScopes(Map tokenResponseParameters) { - if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) { - String scope = getParameterValue(tokenResponseParameters, OAuth2ParameterNames.SCOPE); - return new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); - } - return Collections.emptySet(); - } - - private static String getParameterValue(Map tokenResponseParameters, String parameterName) { - Object obj = tokenResponseParameters.get(parameterName); - return (obj != null) ? obj.toString() : null; - } - - private static long getParameterValue(Map tokenResponseParameters, String parameterName, - long defaultValue) { - long parameterValue = defaultValue; - - Object obj = tokenResponseParameters.get(parameterName); - if (obj != null) { - // Final classes Long and Integer do not need to be coerced - if (obj.getClass() == Long.class) { - parameterValue = (Long) obj; - } - else if (obj.getClass() == Integer.class) { - parameterValue = (Integer) obj; - } - else { - // Attempt to coerce to a long (typically from a String) - try { - parameterValue = Long.parseLong(obj.toString()); - } - catch (NumberFormatException ignored) { - } - } - } - - return parameterValue; - } - - } - - /** - * A {@link Converter} that converts the provided {@link OAuth2AccessTokenResponse} to a - * {@code Map} representation of the OAuth 2.0 Access Token Response parameters. - * - * @author Steve Riesenberg - * @since 5.6 - */ - static final class DefaultOAuth2AccessTokenResponseMapConverter - implements Converter> { - - @Override - public Map convert(OAuth2AccessTokenResponse tokenResponse) { - Map parameters = new HashMap<>(); - parameters.put(OAuth2ParameterNames.ACCESS_TOKEN, tokenResponse.getAccessToken().getTokenValue()); - parameters.put(OAuth2ParameterNames.TOKEN_TYPE, tokenResponse.getAccessToken().getTokenType().getValue()); - parameters.put(OAuth2ParameterNames.EXPIRES_IN, getExpiresIn(tokenResponse)); - if (!CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) { - parameters.put(OAuth2ParameterNames.SCOPE, - StringUtils.collectionToDelimitedString(tokenResponse.getAccessToken().getScopes(), " ")); - } - if (tokenResponse.getRefreshToken() != null) { - parameters.put(OAuth2ParameterNames.REFRESH_TOKEN, tokenResponse.getRefreshToken().getTokenValue()); - } - if (!CollectionUtils.isEmpty(tokenResponse.getAdditionalParameters())) { - for (Map.Entry entry : tokenResponse.getAdditionalParameters().entrySet()) { - parameters.put(entry.getKey(), entry.getValue()); - } - } - return parameters; - } - - private static long getExpiresIn(OAuth2AccessTokenResponse tokenResponse) { - if (tokenResponse.getAccessToken().getExpiresAt() != null) { - return ChronoUnit.SECONDS.between(Instant.now(), tokenResponse.getAccessToken().getExpiresAt()); - } - return -1; - } - - } - -} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java index 23df46b3..865b239a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider; diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java index 73529b58..b252ce5c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;