diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java index 46e5fce86f8..8943fa6dd99 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java @@ -26,6 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; import org.springframework.aop.target.LazyInitTargetSource; @@ -36,6 +37,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; @@ -194,13 +196,11 @@ protected final HttpSecurity getHttp() throws Exception { return http; } - DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor - .postProcess(new DefaultAuthenticationEventPublisher()); + AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher(); localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager(); authenticationBuilder.parentAuthenticationManager(authenticationManager); - authenticationBuilder.authenticationEventPublisher(eventPublisher); Map, Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder, @@ -382,6 +382,11 @@ public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) { return super.eraseCredentials(eraseCredentials); } + @Override + public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) { + authenticationBuilder.authenticationEventPublisher(eventPublisher); + return super.authenticationEventPublisher(eventPublisher); + } }; } @@ -407,6 +412,13 @@ public void setAuthenticationConfiguration( this.authenticationConfiguration = authenticationConfiguration; } + private AuthenticationEventPublisher getAuthenticationEventPublisher() { + if (this.context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) { + return this.context.getBean(AuthenticationEventPublisher.class); + } + return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher()); + } + /** * Creates the shared objects * diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/WebSecurityConfigurerAdapterTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/WebSecurityConfigurerAdapterTests.java index 4aa942e4c9b..8f784aa5134 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/WebSecurityConfigurerAdapterTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/WebSecurityConfigurerAdapterTests.java @@ -33,6 +33,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -40,9 +41,12 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.accept.ContentNegotiationStrategy; @@ -51,8 +55,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.ThrowableAssert.catchThrowable; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -364,4 +371,58 @@ static class DefaultOrderWebSecurityConfig extends WebSecurityConfigurerAdapter @Order static class LowestPriorityWebSecurityConfig extends WebSecurityConfigurerAdapter { } + + // gh-7515 + @Test + public void performWhenUsingAuthenticationEventPublisherBeanThenUses() throws Exception { + this.spring.register(CustomAuthenticationEventPublisherBean.class).autowire(); + + AuthenticationEventPublisher authenticationEventPublisher = + this.spring.getContext().getBean(AuthenticationEventPublisher.class); + + this.mockMvc.perform(get("/") + .with(httpBasic("user", "password"))); + + verify(authenticationEventPublisher).publishAuthenticationSuccess(any(Authentication.class)); + } + + @EnableWebSecurity + static class CustomAuthenticationEventPublisherBean extends WebSecurityConfigurerAdapter { + @Bean + @Override + public UserDetailsService userDetailsService() { + return new InMemoryUserDetailsManager(PasswordEncodedUser.user()); + } + + @Bean + public AuthenticationEventPublisher authenticationEventPublisher() { + return mock(AuthenticationEventPublisher.class); + } + } + + // gh-4400 + @Test + public void performWhenUsingAuthenticationEventPublisherInDslThenUses() throws Exception { + this.spring.register(CustomAuthenticationEventPublisherDsl.class).autowire(); + + AuthenticationEventPublisher authenticationEventPublisher = + CustomAuthenticationEventPublisherDsl.EVENT_PUBLISHER; + + this.mockMvc.perform(get("/") + .with(httpBasic("user", "password"))); // fails since no providers configured + + verify(authenticationEventPublisher).publishAuthenticationFailure( + any(AuthenticationException.class), + any(Authentication.class)); + } + + @EnableWebSecurity + static class CustomAuthenticationEventPublisherDsl extends WebSecurityConfigurerAdapter { + static AuthenticationEventPublisher EVENT_PUBLISHER = mock(AuthenticationEventPublisher.class); + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationEventPublisher(EVENT_PUBLISHER); + } + } } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index 5fdc5cf5ca6..9c45b530068 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -73,6 +73,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManagerResolver; import org.springframework.security.authentication.AuthenticationProvider; @@ -88,6 +89,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; @@ -99,8 +101,8 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; -import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver; import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector; import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; @@ -1091,6 +1093,22 @@ public void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToke .andExpect(invalidTokenHeader("algorithm")); } + // gh-7793 + @Test + public void requestWhenUsingCustomAuthenticationEventPublisherThenUses() throws Exception{ + this.spring.register(CustomAuthenticationEventPublisher.class).autowire(); + + when(bean(JwtDecoder.class).decode(anyString())) + .thenThrow(new JwtException("problem")); + + this.mvc.perform(get("/").with(bearerToken("token"))); + + verifyBean(AuthenticationEventPublisher.class) + .publishAuthenticationFailure( + any(OAuth2AuthenticationException.class), + any(Authentication.class)); + } + @Test public void getWhenCustomJwtAuthenticationManagerThenUsed() throws Exception { this.spring.register(JwtAuthenticationManagerConfig.class, BasicController.class).autowire(); @@ -2015,6 +2033,31 @@ JwtDecoder decoder() throws Exception { } } + @EnableWebSecurity + static class CustomAuthenticationEventPublisher extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .oauth2ResourceServer() + .jwt(); + // @formatter:on + } + + @Bean + JwtDecoder jwtDecoder() { + return mock(JwtDecoder.class); + } + + @Bean + AuthenticationEventPublisher authenticationEventPublisher() { + return mock(AuthenticationEventPublisher.class); + } + } + @EnableWebSecurity static class OpaqueTokenConfig extends WebSecurityConfigurerAdapter { @Override