Explorar el Código

Fix OAuth2AuthorizationServerJacksonModule type validator configuration

Closes gh-18102
Joe Grandja hace 1 semana
padre
commit
e6b4d461e7

+ 12 - 5
core/src/main/java/org/springframework/security/jackson/SecurityJacksonModules.java

@@ -36,10 +36,9 @@ import org.springframework.util.ClassUtils;
 
 /**
  * This utility class will find all the Jackson modules contributed by Spring Security in
- * the classpath (except {@code OAuth2AuthorizationServerJacksonModule} and
- * {@code WebauthnJacksonModule}), enable automatic inclusion of type information and
- * configure a {@link PolymorphicTypeValidator} that handles the validation of class
- * names.
+ * the classpath (except {@code WebauthnJacksonModule}), enable automatic inclusion of
+ * type information and configure a {@link PolymorphicTypeValidator} that handles the
+ * validation of class names.
  *
  * <p>
  * <pre>
@@ -77,6 +76,8 @@ public final class SecurityJacksonModules {
 
 	private static final String oauth2ClientJacksonModuleClass = "org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule";
 
+	private static final String oauth2AuthorizationServerJacksonModuleClass = "org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule";
+
 	private static final String ldapJacksonModuleClass = "org.springframework.security.ldap.jackson.LdapJacksonModule";
 
 	private static final String saml2JacksonModuleClass = "org.springframework.security.saml2.jackson.Saml2JacksonModule";
@@ -87,6 +88,8 @@ public final class SecurityJacksonModules {
 
 	private static final boolean oauth2ClientPresent;
 
+	private static final boolean oauth2AuthorizationServerPresent;
+
 	private static final boolean ldapJacksonPresent;
 
 	private static final boolean saml2JacksonPresent;
@@ -94,11 +97,12 @@ public final class SecurityJacksonModules {
 	private static final boolean casJacksonPresent;
 
 	static {
-
 		ClassLoader classLoader = SecurityJacksonModules.class.getClassLoader();
 		webServletPresent = ClassUtils.isPresent("jakarta.servlet.http.Cookie", classLoader);
 		oauth2ClientPresent = ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
 				classLoader);
+		oauth2AuthorizationServerPresent = ClassUtils
+			.isPresent("org.springframework.security.oauth2.server.authorization.OAuth2Authorization", classLoader);
 		ldapJacksonPresent = ClassUtils.isPresent(ldapJacksonModuleClass, classLoader);
 		saml2JacksonPresent = ClassUtils.isPresent(saml2JacksonModuleClass, classLoader);
 		casJacksonPresent = ClassUtils.isPresent(casJacksonModuleClass, classLoader);
@@ -156,6 +160,9 @@ public final class SecurityJacksonModules {
 		if (oauth2ClientPresent) {
 			addToModulesList(loader, modules, oauth2ClientJacksonModuleClass);
 		}
+		if (oauth2AuthorizationServerPresent) {
+			addToModulesList(loader, modules, oauth2AuthorizationServerJacksonModuleClass);
+		}
 		if (ldapJacksonPresent) {
 			addToModulesList(loader, modules, ldapJacksonModuleClass);
 		}

+ 1 - 0
docs/modules/ROOT/pages/features/integrations/jackson.adoc

@@ -86,6 +86,7 @@ The following Spring Security modules provide Jackson support:
 - spring-security-core (javadoc:org.springframework.security.jackson.CoreJacksonModule[])
 - spring-security-web (javadoc:org.springframework.security.web.jackson.WebJacksonModule[], javadoc:org.springframework.security.web.jackson.WebServletJacksonModule[], javadoc:org.springframework.security.web.server.jackson.WebServerJacksonModule[])
 - spring-security-oauth2-client (javadoc:org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule[])
+- spring-security-oauth2-authorization-server (javadoc:org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule[])
 - spring-security-cas (javadoc:org.springframework.security.cas.jackson.CasJacksonModule[])
 - spring-security-ldap (javadoc:org.springframework.security.ldap.jackson.LdapJacksonModule[])
 - spring-security-saml2 (javadoc:org.springframework.security.saml2.jackson.Saml2JacksonModule[])

+ 1 - 1
oauth2/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle

@@ -13,9 +13,9 @@ dependencies {
 	api "com.nimbusds:nimbus-jose-jwt"
 	api 'tools.jackson.core:jackson-databind'
 
+	optional "com.fasterxml.jackson.core:jackson-databind"
 	optional "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
 	optional "org.springframework:spring-jdbc"
-	optional "com.fasterxml.jackson.core:jackson-databind"
 
 	testImplementation project(":spring-security-test")
 	testImplementation project(path : ':spring-security-oauth2-jose', configuration : 'tests')

+ 3 - 7
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java

@@ -68,7 +68,6 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
 import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
 import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
-import org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;
 import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
@@ -361,7 +360,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
 	/**
 	 * Sets the {@link RowMapper} used for mapping the current row in
 	 * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is
-	 * {@link OAuth2AuthorizationRowMapper}.
+	 * {@link JsonMapperOAuth2AuthorizationRowMapper}.
 	 * @param authorizationRowMapper the {@link RowMapper} used for mapping the current
 	 * row in {@code ResultSet} to {@link OAuth2Authorization}
 	 */
@@ -373,7 +372,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
 	/**
 	 * Sets the {@code Function} used for mapping {@link OAuth2Authorization} to a
 	 * {@code List} of {@link SqlParameterValue}. The default is
-	 * {@link OAuth2AuthorizationParametersMapper}.
+	 * {@link JsonMapperOAuth2AuthorizationParametersMapper}.
 	 * @param authorizationParametersMapper the {@code Function} used for mapping
 	 * {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue}
 	 */
@@ -743,10 +742,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
 
 		static JsonMapper createJsonMapper() {
 			List<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());
-			return JsonMapper.builder()
-				.addModules(modules)
-				.addModules(new OAuth2AuthorizationServerJacksonModule())
-				.build();
+			return JsonMapper.builder().addModules(modules).build();
 		}
 
 	}

+ 10 - 16
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModule.java

@@ -18,13 +18,11 @@ package org.springframework.security.oauth2.server.authorization.jackson;
 
 import java.net.URL;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import tools.jackson.core.Version;
-import tools.jackson.databind.DefaultTyping;
-import tools.jackson.databind.cfg.MapperBuilder;
 import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
 
-import org.springframework.security.jackson.CoreJacksonModule;
+import org.springframework.security.jackson.SecurityJacksonModule;
+import org.springframework.security.jackson.SecurityJacksonModules;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
 import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
@@ -37,20 +35,22 @@ import org.springframework.security.oauth2.server.authorization.settings.OAuth2T
  * registers the following mix-in annotations:
  *
  * <ul>
- * <li>{@link OAuth2TokenExchangeActor}</li>
+ * <li>{@link OAuth2TokenExchangeActorMixin}</li>
  * <li>{@link OAuth2AuthorizationRequestMixin}</li>
  * <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>
  * <li>{@link JwsAlgorithmMixin}</li>
  * <li>{@link OAuth2TokenFormatMixin}</li>
  * </ul>
  *
- * If not already enabled, default typing will be automatically enabled as type info is
- * required to properly serialize/deserialize objects. In order to use this module just
- * add it to your {@code JsonMapper.Builder} configuration.
+ * <p>
+ * The recommended way to configure it is to use {@link SecurityJacksonModules} in order
+ * to enable properly automatic inclusion of type information with related validation.
  *
  * <pre>
+ *     ClassLoader loader = getClass().getClassLoader();
  *     JsonMapper mapper = JsonMapper.builder()
- *             .addModules(new OAuth2AuthorizationServerJacksonModule()).build;
+ * 				.addModules(SecurityJacksonModules.getModules(loader))
+ * 				.build();
  * </pre>
  *
  * @author Sebastien Deleuze
@@ -58,7 +58,7 @@ import org.springframework.security.oauth2.server.authorization.settings.OAuth2T
  * @since 7.0
  */
 @SuppressWarnings("serial")
-public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
+public class OAuth2AuthorizationServerJacksonModule extends SecurityJacksonModule {
 
 	public OAuth2AuthorizationServerJacksonModule() {
 		super(OAuth2AuthorizationServerJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
@@ -66,7 +66,6 @@ public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
 
 	@Override
 	public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
-		super.configurePolymorphicTypeValidator(builder);
 		builder.allowIfSubType(OAuth2TokenFormat.class)
 			.allowIfSubType(OAuth2TokenExchangeActor.class)
 			.allowIfSubType(OAuth2TokenExchangeCompositeAuthenticationToken.class)
@@ -78,11 +77,6 @@ public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
 
 	@Override
 	public void setupModule(SetupContext context) {
-		super.setupModule(context);
-		BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder();
-		this.configurePolymorphicTypeValidator(builder);
-		((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(), DefaultTyping.NON_FINAL,
-				JsonTypeInfo.As.PROPERTY);
 		context.setMixIn(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);
 		context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
 		context.setMixIn(OAuth2TokenExchangeCompositeAuthenticationToken.class,

+ 28 - 2
oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java

@@ -16,6 +16,7 @@
 
 package org.springframework.security.oauth2.server.authorization;
 
+import java.security.Principal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Timestamp;
@@ -45,6 +46,7 @@ import org.springframework.jdbc.core.SqlParameterValue;
 import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
 import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
 import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.jackson.SecurityJacksonModules;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -58,6 +60,7 @@ import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
 import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
 import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
 import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
@@ -227,10 +230,11 @@ public class JdbcOAuth2AuthorizationServiceTests {
 			.build();
 
 		RowMapper<OAuth2Authorization> authorizationRowMapper = spy(
-				new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(this.registeredClientRepository));
+				new JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationRowMapper(
+						this.registeredClientRepository));
 		this.authorizationService.setAuthorizationRowMapper(authorizationRowMapper);
 		Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper = spy(
-				new JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper());
+				new JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationParametersMapper());
 		this.authorizationService.setAuthorizationParametersMapper(authorizationParametersMapper);
 
 		this.authorizationService.save(originalAuthorization);
@@ -461,6 +465,28 @@ public class JdbcOAuth2AuthorizationServiceTests {
 		assertThat(result).isNull();
 	}
 
+	// gh-18102
+	@Test
+	public void findByTokenWhenPrincipalHasWebAuthenticationDetailsThenDeserializes() {
+		given(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);
+
+		String state = "state";
+		TestingAuthenticationToken principal = new TestingAuthenticationToken(PRINCIPAL_NAME, "credentials");
+		principal.setDetails(new WebAuthenticationDetails("remoteAddress", "sessionId"));
+
+		OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
+			.id(ID)
+			.principalName(PRINCIPAL_NAME)
+			.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+			.attribute(OAuth2ParameterNames.STATE, state)
+			.attribute(Principal.class.getName(), principal)
+			.build();
+		this.authorizationService.save(authorization);
+
+		OAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);
+		assertThat(authorization).isEqualTo(result);
+	}
+
 	@Test
 	public void tableDefinitionWhenCustomThenAbleToOverride() {
 		given(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);

+ 5 - 3
oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModuleTests.java

@@ -27,20 +27,20 @@ import tools.jackson.databind.json.JsonMapper;
 
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.jackson.SecurityJacksonModules;
 import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
 import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
 import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
 import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
 import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
-import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
 import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
 import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
- * Tests for {@link OAuth2AuthorizationServerJackson2Module}.
+ * Tests for {@link OAuth2AuthorizationServerJacksonModule}.
  *
  * @author Steve Riesenberg
  * @author Joe Grandja
@@ -55,7 +55,9 @@ public class OAuth2AuthorizationServerJacksonModuleTests {
 
 	@BeforeEach
 	public void setup() {
-		this.mapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
+		this.mapper = JsonMapper.builder()
+			.addModules(SecurityJacksonModules.getModules(getClass().getClassLoader()))
+			.build();
 	}
 
 	@Test