From 112480af48368d4273d4e4528c5e9d29d485ba56 Mon Sep 17 00:00:00 2001 From: Florian Lopes Date: Sun, 17 Dec 2017 10:51:31 +0100 Subject: [PATCH] Fixes #226 - Configure filterProcessesUrl via SpringSocialConfigurer - Add the ability to configure filterProcessesUrl of SocialAuthentication via SpringSocialConfigurer - Add SpringSocialConfigurer#configure test --- .../security/SpringSocialConfigurer.java | 55 +++++---- .../security/SpringSocialConfigurerTest.java | 106 ++++++++++++++++++ 2 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 spring-social-security/src/test/java/org/springframework/social/security/SpringSocialConfigurerTest.java diff --git a/spring-social-security/src/main/java/org/springframework/social/security/SpringSocialConfigurer.java b/spring-social-security/src/main/java/org/springframework/social/security/SpringSocialConfigurer.java index 3c5f662d6..f897ac2ec 100644 --- a/spring-social-security/src/main/java/org/springframework/social/security/SpringSocialConfigurer.java +++ b/spring-social-security/src/main/java/org/springframework/social/security/SpringSocialConfigurer.java @@ -29,7 +29,7 @@ /** * Configurer that adds {@link SocialAuthenticationFilter} to Spring Security's filter chain. * Used with Spring Security 3.2's Java-based configuration support, when overriding WebSecurityConfigurerAdapter#configure(HttpSecurity): - * + * *
  * protected void configure(HttpSecurity http) throws Exception {
  *   http.
@@ -38,21 +38,23 @@
  *        .apply(new SpringSocialConfigurer());
  * }
  * 
- * + * * @author Craig Walls */ public class SpringSocialConfigurer extends SecurityConfigurerAdapter { private UserIdSource userIdSource; - + private String postLoginUrl; - + private String postFailureUrl; private String signupUrl; private String connectionAddedRedirectUrl; + private String filterProcessesUrl; + private boolean alwaysUsePostLoginUrl = false; /** @@ -62,30 +64,30 @@ public class SpringSocialConfigurer extends SecurityConfigurerAdapter T getDependency(ApplicationContext applicationContext, Class depe throw new IllegalStateException("SpringSocialConfigurer depends on " + dependencyType.getName() +". No single bean of that type found in application context.", e); } } - + /** * Sets the {@link UserIdSource} to use for authentication. Defaults to {@link AuthenticationNameUserIdSource}. * @param userIdSource the UserIdSource to use when authenticating @@ -121,27 +127,27 @@ public SpringSocialConfigurer userIdSource(UserIdSource userIdSource) { this.userIdSource = userIdSource; return this; } - + /** * Sets the URL to land on after a successful login. * @param postLoginUrl the URL to redirect to after a successful login - * @return this SpringSocialConfigurer for chained configuration + * @return this SpringSocialConfigurer for chained configuration */ public SpringSocialConfigurer postLoginUrl(String postLoginUrl) { this.postLoginUrl = postLoginUrl; return this; } - + /** * If true, always redirect to postLoginUrl, even if a pre-signin target is in the request cache. * @param alwaysUsePostLoginUrl if true, always redirect to the postLoginUrl - * @return this SpringSocialConfigurer for chained configuration + * @return this SpringSocialConfigurer for chained configuration */ public SpringSocialConfigurer alwaysUsePostLoginUrl(boolean alwaysUsePostLoginUrl) { this.alwaysUsePostLoginUrl = alwaysUsePostLoginUrl; return this; } - + /** * Sets the URL to redirect to if authentication fails or if authorization is denied by the user. * @param postFailureUrl the URL to redirect to after an authentication fail or authorization deny @@ -172,4 +178,13 @@ public SpringSocialConfigurer connectionAddedRedirectUrl(String connectionAddedR return this; } + /** + * Sets the URL that determines if social authentication is required. + * @param filterProcessesUrl the URL that will initiate the social authentication process + * @return this SpringSocialConfigurer for chained configuration + */ + public SpringSocialConfigurer filterProcessesUrl(String filterProcessesUrl) { + this.filterProcessesUrl = filterProcessesUrl; + return this; + } } diff --git a/spring-social-security/src/test/java/org/springframework/social/security/SpringSocialConfigurerTest.java b/spring-social-security/src/test/java/org/springframework/social/security/SpringSocialConfigurerTest.java new file mode 100644 index 000000000..2d209e202 --- /dev/null +++ b/spring-social-security/src/test/java/org/springframework/social/security/SpringSocialConfigurerTest.java @@ -0,0 +1,106 @@ +package org.springframework.social.security; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.social.connect.UsersConnectionRepository; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * @author Florian Lopes + */ +@RunWith(SpringRunner.class) +public class SpringSocialConfigurerTest { + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + @Autowired + private UsersConnectionRepository usersConnectionRepository; + + @Autowired + private SocialAuthenticationServiceLocator socialAuthenticationServiceLocator; + + @Test + public void testConfigure() { + final SecurityFilterChain defaultFilterChain = this.springSecurityFilterChain.getFilterChains().get(0); + assertTrue(defaultFilterChain.getFilters().stream().anyMatch(filter -> filter instanceof SocialAuthenticationFilter)); + + final SocialAuthenticationFilter socialAuthenticationFilter = + (SocialAuthenticationFilter) defaultFilterChain.getFilters() + .stream().filter(filter -> filter instanceof SocialAuthenticationFilter).findFirst().orElse(null); + assertNotNull(socialAuthenticationFilter); + + assertTrue(ReflectionTestUtils.getField(socialAuthenticationFilter, "userIdSource") instanceof AuthenticationNameUserIdSource); + + final ProviderManager providerManager = + (ProviderManager) ReflectionTestUtils.getField(socialAuthenticationFilter, "authenticationManager"); + assertTrue(providerManager.getProviders().stream().anyMatch(authenticationProvider -> authenticationProvider instanceof SocialAuthenticationProvider)); + + assertNotNull(ReflectionTestUtils.getField(socialAuthenticationFilter, "rememberMeServices")); + final SocialAuthenticationFailureHandler failureHandler = + (SocialAuthenticationFailureHandler) ReflectionTestUtils.getField(socialAuthenticationFilter, "failureHandler"); + + assertEquals("/postFailure", ReflectionTestUtils.getField(failureHandler.getDelegate(), "defaultFailureUrl")); + final AuthenticationSuccessHandler successHandler = + (AuthenticationSuccessHandler) ReflectionTestUtils.getField(socialAuthenticationFilter, "successHandler"); + assertEquals("/postLogin", ReflectionTestUtils.getField(successHandler, "defaultTargetUrl")); + assertEquals("/social-login", ReflectionTestUtils.getField(socialAuthenticationFilter, "filterProcessesUrl")); + assertEquals("/connectionAdded", ReflectionTestUtils.getField(socialAuthenticationFilter, "connectionAddedRedirectUrl")); + assertEquals("/signup", ReflectionTestUtils.getField(socialAuthenticationFilter, "signupUrl")); + + assertSame(this.usersConnectionRepository, socialAuthenticationFilter.getUsersConnectionRepository()); + assertSame(this.socialAuthenticationServiceLocator, socialAuthenticationFilter.getAuthServiceLocator()); + } + + @EnableWebSecurity + @Configuration + static class SpringSocialSecurityConfig extends WebSecurityConfigurerAdapter { + + // @formatter:off + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .rememberMe() + .and() + .apply(new SpringSocialConfigurer() + .userIdSource(new AuthenticationNameUserIdSource()) + .postLoginUrl("/postLogin") + .postFailureUrl("/postFailure") + .signupUrl("/signup") + .connectionAddedRedirectUrl("/connectionAdded") + .filterProcessesUrl("/social-login")); + } + // @formatter:on + + @Bean + public UsersConnectionRepository usersConnectionRepository() { + return mock(UsersConnectionRepository.class); + } + + @Bean + public SocialUserDetailsService socialUserDetailsService() { + return mock(SocialUserDetailsService.class); + } + + @Bean + public SocialAuthenticationServiceLocator socialAuthenticationServiceLocator() { + return mock(SocialAuthenticationServiceLocator.class); + } + } +}