Browse Source

Relax ObjectPostProcessor Type Constraints

Closes gh-17175

Signed-off-by: Maciej Kowalski <f.kowal@gmail.com>
Maciej Kowalski 3 months ago
parent
commit
46283b3452

+ 2 - 4
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@@ -404,11 +404,9 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
 				oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
 				oidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper);
 			}
-			oidcAuthorizationCodeAuthenticationProvider = this.postProcess(oidcAuthorizationCodeAuthenticationProvider);
-			http.authenticationProvider(oidcAuthorizationCodeAuthenticationProvider);
+			http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
 
-			oidcAuthorizedClientRefreshedEventListener = this.postProcess(oidcAuthorizedClientRefreshedEventListener);
-			registerDelegateApplicationListener(oidcAuthorizedClientRefreshedEventListener);
+			registerDelegateApplicationListener(this.postProcess(oidcAuthorizedClientRefreshedEventListener));
 			configureOidcUserRefreshedEventListener(http);
 		}
 		else {

+ 76 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java

@@ -43,7 +43,10 @@ import org.springframework.http.MediaType;
 import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.ObjectPostProcessor;
 import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -52,6 +55,7 @@ import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
 import org.springframework.security.context.DelegatingApplicationListener;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -214,6 +218,28 @@ public class OAuth2LoginConfigurerTests {
 			.hasToString("OAUTH2_USER");
 	}
 
+	// gh-17175
+	@Test
+	public void postProcessorSucceedsWhenProcessorReturnsAuthenticationProvider() throws Exception {
+		loadConfig(OAuth2LoginConfigCustomWithPostProcessor.class);
+		// setup authorization request
+		OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();
+		this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);
+		// setup authentication parameters
+		this.request.setParameter("code", "code123");
+		this.request.setParameter("state", authorizationRequest.getState());
+		// perform test
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
+		// assertions
+		Authentication authentication = this.securityContextRepository
+				.loadContext(new HttpRequestResponseHolder(this.request, this.response))
+				.getAuthentication();
+		assertThat(authentication.getAuthorities()).hasSize(1);
+		assertThat(authentication.getAuthorities()).first()
+				.isInstanceOf(OAuth2UserAuthority.class)
+				.hasToString("OAUTH2_USER");
+	}
+
 	@Test
 	public void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
 		loadConfig(OAuth2LoginConfig.class, SecurityContextChangedListenerConfig.class);
@@ -1307,6 +1333,56 @@ public class OAuth2LoginConfigurerTests {
 
 	}
 
+	@Configuration
+	@EnableWebSecurity
+	static class OAuth2LoginConfigCustomWithPostProcessor
+			extends CommonLambdaSecurityFilterChainConfig {
+
+		private ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(
+				GOOGLE_CLIENT_REGISTRATION);
+
+		OAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+					.oauth2Login((oauth2Login) ->
+							oauth2Login
+									.clientRegistrationRepository(this.clientRegistrationRepository)
+//									.authorizedClientRepository(this.authorizedClientRepository)
+									.withObjectPostProcessor(new CustomProcessor())
+					);
+			// @formatter:on
+			return super.configureFilterChain(http);
+		}
+
+		class CustomProcessor implements ObjectPostProcessor<AuthenticationProvider> {
+			@Override
+			public <O extends AuthenticationProvider> O postProcess(O object) {
+				AuthenticationProvider p = new NoopWrapperProvider(object);
+
+				return (O) p;
+			}
+		}
+
+		record NoopWrapperProvider(
+			AuthenticationProvider delegate
+		) implements AuthenticationProvider {
+
+			@Override
+			public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+				return delegate.authenticate(authentication);
+			}
+
+			@Override
+			public boolean supports(Class<?> authentication) {
+				return delegate.supports(authentication);
+			}
+		}
+
+	}
+
 	private abstract static class CommonSecurityFilterChainConfig {
 
 		SecurityFilterChain configureFilterChain(HttpSecurity http) throws Exception {