Browse Source

Fix OAuth2AuthorizationServerJacksonModule type validator configuration

Closes gh-18102
pull/18122/head
Joe Grandja 2 months ago
parent
commit
e6b4d461e7
  1. 17
      core/src/main/java/org/springframework/security/jackson/SecurityJacksonModules.java
  2. 1
      docs/modules/ROOT/pages/features/integrations/jackson.adoc
  3. 2
      oauth2/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle
  4. 10
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java
  5. 26
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModule.java
  6. 30
      oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java
  7. 8
      oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModuleTests.java

17
core/src/main/java/org/springframework/security/jackson/SecurityJacksonModules.java

@ -36,10 +36,9 @@ import org.springframework.util.ClassUtils; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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
docs/modules/ROOT/pages/features/integrations/jackson.adoc

@ -86,6 +86,7 @@ The following Spring Security modules provide Jackson support: @@ -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[])

2
oauth2/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle

@ -13,9 +13,9 @@ dependencies { @@ -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')

10
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; @@ -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 @@ -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 @@ -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 @@ -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();
}
}

26
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; @@ -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 @@ -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 @@ -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 { @@ -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 { @@ -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,

30
oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java

@ -16,6 +16,7 @@ @@ -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; @@ -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 @@ -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 { @@ -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 { @@ -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);

8
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; @@ -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 { @@ -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

Loading…
Cancel
Save