From 39ba1006ba1ef872166691cdad2fc257fb9a9520 Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Thu, 27 Jun 2019 11:26:23 -0400 Subject: [PATCH] Migrate FormLoginConfigurerSpec groovy->java Issue: gh-4939 --- .../FormLoginConfigurerSpec.groovy | 368 --------------- .../configurers/FormLoginConfigurerTests.java | 443 +++++++++++++++++- 2 files changed, 442 insertions(+), 369 deletions(-) delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerSpec.groovy diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerSpec.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerSpec.groovy deleted file mode 100644 index 5b549d8fb2..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerSpec.groovy +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.config.annotation.web.configurers - -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.annotation.Configuration -import org.springframework.http.HttpMethod -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.authentication.BadCredentialsException -import org.springframework.security.config.annotation.AnyObjectPostProcessor -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.builders.WebSecurity -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.web.AuthenticationEntryPoint -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.PortMapper -import org.springframework.security.web.WebAttributes -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor -import org.springframework.security.web.authentication.AnonymousAuthenticationFilter -import org.springframework.security.web.authentication.AuthenticationFailureHandler -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import org.springframework.security.web.authentication.logout.LogoutFilter -import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy -import org.springframework.security.web.context.SecurityContextPersistenceFilter -import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter -import org.springframework.security.web.csrf.CsrfFilter -import org.springframework.security.web.header.HeaderWriterFilter -import org.springframework.security.web.savedrequest.RequestCacheAwareFilter -import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter -import org.springframework.security.web.session.SessionManagementFilter -import org.springframework.security.web.util.matcher.AnyRequestMatcher -import org.springframework.test.util.ReflectionTestUtils - -import spock.lang.Unroll - -/** - * - * @author Rob Winch - */ -class FormLoginConfigurerSpec extends BaseSpringSpec { - def "Form Login"() { - when: "load formLogin()" - context = new AnnotationConfigApplicationContext(FormLoginConfig) - - then: "FilterChains configured correctly" - def filterChains = filterChains() - filterChains.size() == 2 - filterChains[0].requestMatcher.pattern == '/resources/**' - filterChains[0].filters.empty - filterChains[1].requestMatcher instanceof AnyRequestMatcher - filterChains[1].filters.collect { it.class.name.contains('$') ? it.class.superclass : it.class } == - [WebAsyncManagerIntegrationFilter, SecurityContextPersistenceFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, - RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, - AnonymousAuthenticationFilter, SessionManagementFilter, ExceptionTranslationFilter, FilterSecurityInterceptor ] - - and: "UsernamePasswordAuthentictionFilter is configured correctly" - UsernamePasswordAuthenticationFilter authFilter = findFilter(UsernamePasswordAuthenticationFilter,1) - authFilter.usernameParameter == "username" - authFilter.passwordParameter == "password" - authFilter.failureHandler.defaultFailureUrl == "/login?error" - authFilter.successHandler.defaultTargetUrl == "/" - authFilter.requiresAuthentication(new MockHttpServletRequest(servletPath : "/login", method: "POST"), new MockHttpServletResponse()) - !authFilter.requiresAuthentication(new MockHttpServletRequest(servletPath : "/login", method: "GET"), new MockHttpServletResponse()) - - and: "Exception handling is configured correctly" - AuthenticationEntryPoint authEntryPoint = filterChains[1].filters.find { it instanceof ExceptionTranslationFilter}.authenticationEntryPoint - MockHttpServletResponse response = new MockHttpServletResponse() - authEntryPoint.commence(new MockHttpServletRequest(servletPath: "/private/"), response, new BadCredentialsException("")) - response.redirectedUrl == "http://localhost/login" - } - - @EnableWebSecurity - static class FormLoginConfig extends BaseWebConfig { - @Override - public void configure(WebSecurity web) throws Exception { - web - .ignoring() - .antMatchers("/resources/**"); - } - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .loginPage("/login") - } - } - - def "FormLogin.permitAll()"() { - when: "load formLogin() with permitAll" - context = new AnnotationConfigApplicationContext(FormLoginConfigPermitAll) - FilterChainProxy filterChain = context.getBean(FilterChainProxy) - MockHttpServletResponse response = new MockHttpServletResponse() - request = new MockHttpServletRequest(servletPath : servletPath, requestURI: servletPath, queryString: query, method: method) - setupCsrf() - - then: "the formLogin URLs are granted access" - filterChain.doFilter(request, response, new MockFilterChain()) - response.redirectedUrl == redirectUrl - - where: - servletPath | method | query | redirectUrl - "/login" | "GET" | null | null - "/login" | "POST" | null | "/login?error" - "/login" | "GET" | "error" | null - } - - @EnableWebSecurity - static class FormLoginConfigPermitAll extends BaseWebConfig { - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .permitAll() - } - } - - @Unroll - def "FormLogin loginConventions changes defaults"() { - when: "load formLogin() with permitAll" - loadConfig(FormLoginDefaultsConfig) - MockHttpServletResponse response = new MockHttpServletResponse() - request = new MockHttpServletRequest(servletPath : servletPath, requestURI: servletPath, queryString: query, method: method) - setupCsrf() - - then: "the other default login/logout URLs are updated and granted access" - springSecurityFilterChain.doFilter(request, response, new MockFilterChain()) - response.redirectedUrl == redirectUrl - - where: - servletPath | method | query | redirectUrl - "/authenticate" | "GET" | null | null - "/authenticate" | "POST" | null | "/authenticate?error" - "/authenticate" | "GET" | "error" | null - "/logout" | "POST" | null | "/authenticate?logout" - "/authenticate" | "GET" | "logout"| null - } - - @EnableWebSecurity - static class FormLoginDefaultsConfig extends BaseWebConfig { - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .loginPage("/authenticate") - .permitAll() - .and() - .logout() - .permitAll() - } - } - - def "FormLogin loginProcessingUrl"() { - setup: - loadConfig(FormLoginLoginProcessingUrlConfig) - request.servletPath = "/loginCheck" - request.method = "POST" - request.parameters.username = ["user"] as String[] - request.parameters.password = ["password"] as String[] - when: - springSecurityFilterChain.doFilter(request, response, new MockFilterChain()) - then: - response.redirectedUrl == "/" - } - - @EnableWebSecurity - static class FormLoginLoginProcessingUrlConfig extends BaseWebConfig { - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .formLogin() - .loginProcessingUrl("/loginCheck") - .loginPage("/login") - //.failureUrl("/loginFailure") - .defaultSuccessUrl("/", true) - .passwordParameter("password") - .usernameParameter("username") - .permitAll() - .and() - .logout() - .logoutSuccessUrl("/login") - .logoutUrl("/logout") - .deleteCookies("JSESSIONID") - } - } - - def "FormLogin uses PortMapper"() { - when: "load formLogin() with permitAll" - FormLoginUsesPortMapperConfig.PORT_MAPPER = Mock(PortMapper) - loadConfig(FormLoginUsesPortMapperConfig) - then: "the formLogin URLs are granted access" - findFilter(ExceptionTranslationFilter).authenticationEntryPoint.portMapper == FormLoginUsesPortMapperConfig.PORT_MAPPER - } - - @EnableWebSecurity - static class FormLoginUsesPortMapperConfig extends BaseWebConfig { - static PortMapper PORT_MAPPER - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .permitAll() - .and() - .portMapper() - .portMapper(PORT_MAPPER) - } - } - - def "FormLogin permitAll ignores failureUrl when failureHandler set"() { - setup: - PermitAllIgnoresFailureHandlerConfig.FAILURE_HANDLER = Mock(AuthenticationFailureHandler) - loadConfig(PermitAllIgnoresFailureHandlerConfig) - FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy) - when: "access default failureUrl and configured explicit FailureHandler" - MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",requestURI:"/login",queryString:"error",method:"GET") - MockHttpServletResponse response = new MockHttpServletResponse() - springSecurityFilterChain.doFilter(request,response,new MockFilterChain()) - then: "access is not granted to the failure handler (sent to login page)" - response.status == 302 - } - - @EnableWebSecurity - static class PermitAllIgnoresFailureHandlerConfig extends BaseWebConfig { - static AuthenticationFailureHandler FAILURE_HANDLER - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .failureHandler(FAILURE_HANDLER) - .permitAll() - } - } - - def "duplicate formLogin does not override"() { - setup: - DuplicateInvocationsDoesNotOverrideConfig.FAILURE_HANDLER = Mock(AuthenticationFailureHandler) - when: - loadConfig(DuplicateInvocationsDoesNotOverrideConfig) - then: - findFilter(UsernamePasswordAuthenticationFilter).usernameParameter == "custom-username" - } - - def "FormLogin permitAll uses Failure Forward Url when ForwardAuthenticationFailureHandler set"() { - setup: - loadConfig(FormLoginUserForwardAuthenticationSuccessAndFailureConfig) - FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy) - when: "access configured explicit ForwardFailureFailureHandler" - MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",method:"POST") - request.setParameter("username", "user"); - request.setParameter("password", "invalidpassword"); - MockHttpServletResponse response = new MockHttpServletResponse() - springSecurityFilterChain.doFilter(request,response,new MockFilterChain()) - then: "access is granted to the failure handler" - response.status == 200 - response.forwardedUrl == "/failure_forward_url" - request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null - } - - def "FormLogin permitAll uses Success Forward Url when ForwardAuthenticationSuccessHandler set"() { - setup: - loadConfig(FormLoginUserForwardAuthenticationSuccessAndFailureConfig) - FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy) - when: "access configured explicit ForwardSuccessAuthenticationHandler" - MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",method:"POST") - request.setParameter("username", "user"); - request.setParameter("password", "password"); - MockHttpServletResponse response = new MockHttpServletResponse() - springSecurityFilterChain.doFilter(request,response,new MockFilterChain()) - then: "access is granted to the success handler" - response.status == 200 - response.forwardedUrl == "/success_forward_url" - } - - @EnableWebSecurity - static class FormLoginUserForwardAuthenticationSuccessAndFailureConfig extends BaseWebConfig { - - @Override - protected void configure(HttpSecurity http) { - http.csrf() - .disable() - .authorizeRequests() - .anyRequest().authenticated() - .and() - .formLogin() - .failureForwardUrl("/failure_forward_url") - .successForwardUrl("/success_forward_url") - .permitAll() - - } - } - - @EnableWebSecurity - static class DuplicateInvocationsDoesNotOverrideConfig extends BaseWebConfig { - static AuthenticationFailureHandler FAILURE_HANDLER - - @Override - protected void configure(HttpSecurity http) { - http - .formLogin() - .usernameParameter("custom-username") - .and() - .formLogin() - } - } - - def "formLogin ObjectPostProcessor"() { - setup: "initialize the AUTH_FILTER as a mock" - AnyObjectPostProcessor opp = Mock() - HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:]) - when: - http - .exceptionHandling() - .and() - .formLogin() - .and() - .build() - - then: "UsernamePasswordAuthenticationFilter is registered with ObjectPostProcessor" - 1 * opp.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o} - and: "LoginUrlAuthenticationEntryPoint is registered with ObjectPostProcessor" - 1 * opp.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o} - and: "ExceptionTranslationFilter is registered with ObjectPostProcessor" - 1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o} - } -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java index a80a0feaa7..4211806094 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,22 +20,40 @@ import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; 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.config.users.AuthenticationTestConfiguration; +import org.springframework.security.core.userdetails.PasswordEncodedUser; +import org.springframework.security.web.PortMapper; +import org.springframework.security.web.access.ExceptionTranslationFilter; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * @author Rob Winch + * @author Eleftheria Stein * @since 5.1 */ public class FormLoginConfigurerTests { @@ -91,4 +109,427 @@ public class FormLoginConfigurerTests { return mock(RequestCache.class); } } + + @Test + public void loginWhenFormLoginConfiguredThenHasDefaultUsernameAndPasswordParameterNames() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(formLogin().user("username", "user").password("password", "password")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/")); + } + + @Test + public void loginWhenFormLoginConfiguredThenHasDefaultFailureUrl() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(formLogin().user("invalid")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/login?error")); + } + + @Test + public void loginWhenFormLoginConfiguredThenHasDefaultSuccessUrl() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(formLogin()) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/")); + } + + @Test + public void getLoginPageWhenFormLoginConfiguredThenNotSecured() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(get("/login")) + .andExpect(status().isFound()); + } + + @Test + public void loginWhenFormLoginConfiguredThenSecured() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(post("/login")) + .andExpect(status().isForbidden()); + } + + @Test + public void requestProtectedWhenFormLoginConfiguredThenRedirectsToLogin() throws Exception { + this.spring.register(FormLoginConfig.class).autowire(); + + this.mockMvc.perform(get("/private")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + @EnableWebSecurity + static class FormLoginConfig extends WebSecurityConfigurerAdapter { + @Override + public void configure(WebSecurity web) throws Exception { + // @formatter:off + web + .ignoring() + .antMatchers("/resources/**"); + // @formatter:on + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .loginPage("/login"); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception { + this.spring.register(FormLoginConfigPermitAll.class).autowire(); + + this.mockMvc.perform(get("/login")) + .andExpect(status().isOk()) + .andExpect(redirectedUrl(null)); + } + + @Test + public void getLoginPageWithErrorQueryWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception { + this.spring.register(FormLoginConfigPermitAll.class).autowire(); + + this.mockMvc.perform(get("/login?error")) + .andExpect(status().isOk()) + .andExpect(redirectedUrl(null)); + } + + @Test + public void loginWhenFormLoginPermitAllAndInvalidUserThenRedirectsToLoginPageWithError() throws Exception { + this.spring.register(FormLoginConfigPermitAll.class).autowire(); + + this.mockMvc.perform(formLogin().user("invalid")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/login?error")); + } + + @EnableWebSecurity + static class FormLoginConfigPermitAll extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .permitAll(); + // @formatter:on + } + } + + @Test + public void getLoginPageWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception { + this.spring.register(FormLoginDefaultsConfig.class).autowire(); + + this.mockMvc.perform(get("/authenticate")) + .andExpect(redirectedUrl(null)); + } + + @Test + public void getLoginPageWithErrorQueryWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception { + this.spring.register(FormLoginDefaultsConfig.class).autowire(); + + this.mockMvc.perform(get("/authenticate?error")) + .andExpect(redirectedUrl(null)); + } + + @Test + public void loginWhenCustomLoginPageAndInvalidUserThenRedirectsToCustomLoginPageWithError() throws Exception { + this.spring.register(FormLoginDefaultsConfig.class).autowire(); + + this.mockMvc.perform(formLogin("/authenticate").user("invalid")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/authenticate?error")); + } + + @Test + public void logoutWhenCustomLoginPageThenRedirectsToCustomLoginPage() throws Exception { + this.spring.register(FormLoginDefaultsConfig.class).autowire(); + + this.mockMvc.perform(logout()) + .andExpect(redirectedUrl("/authenticate?logout")); + } + + @Test + public void getLoginPageWithLogoutQueryWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception { + this.spring.register(FormLoginDefaultsConfig.class).autowire(); + + this.mockMvc.perform(get("/authenticate?logout")) + .andExpect(redirectedUrl(null)); + } + + @EnableWebSecurity + static class FormLoginDefaultsConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .loginPage("/authenticate") + .permitAll() + .and() + .logout() + .permitAll(); + // @formatter:on + } + } + + @Test + public void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception { + this.spring.register(FormLoginLoginProcessingUrlConfig.class).autowire(); + + this.mockMvc.perform(formLogin("/loginCheck")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/")); + } + + @EnableWebSecurity + static class FormLoginLoginProcessingUrlConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .formLogin() + .loginProcessingUrl("/loginCheck") + .loginPage("/login") + .defaultSuccessUrl("/", true) + .passwordParameter("password") + .usernameParameter("username") + .permitAll() + .and() + .logout() + .logoutSuccessUrl("/login") + .logoutUrl("/logout") + .deleteCookies("JSESSIONID"); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception { + FormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class); + when(FormLoginUsesPortMapperConfig.PORT_MAPPER.lookupHttpsPort(any())).thenReturn(9443); + this.spring.register(FormLoginUsesPortMapperConfig.class).autowire(); + + this.mockMvc.perform(get("http://localhost:9090")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("https://localhost:9443/login")); + + verify(FormLoginUsesPortMapperConfig.PORT_MAPPER).lookupHttpsPort(any()); + } + + @EnableWebSecurity + static class FormLoginUsesPortMapperConfig extends WebSecurityConfigurerAdapter { + static PortMapper PORT_MAPPER; + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .permitAll() + .and() + .portMapper() + .portMapper(PORT_MAPPER); + // @formatter:on + LoginUrlAuthenticationEntryPoint authenticationEntryPoint = + (LoginUrlAuthenticationEntryPoint) http.getConfigurer(FormLoginConfigurer.class).getAuthenticationEntryPoint(); + authenticationEntryPoint.setForceHttps(true); + } + } + + @Test + public void failureUrlWhenPermitAllAndFailureHandlerThenSecured() throws Exception { + this.spring.register(PermitAllIgnoresFailureHandlerConfig.class).autowire(); + + this.mockMvc.perform(get("/login?error")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + @EnableWebSecurity + static class PermitAllIgnoresFailureHandlerConfig extends WebSecurityConfigurerAdapter { + static AuthenticationFailureHandler FAILURE_HANDLER = mock(AuthenticationFailureHandler.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .failureHandler(FAILURE_HANDLER) + .permitAll(); + // @formatter:on + } + } + + @Test + public void formLoginWhenInvokedTwiceThenUsesOriginalUsernameParameter() throws Exception { + this.spring.register(DuplicateInvocationsDoesNotOverrideConfig.class).autowire(); + + this.mockMvc.perform(formLogin().user("custom-username", "user")) + .andExpect(authenticated()); + } + + @EnableWebSecurity + static class DuplicateInvocationsDoesNotOverrideConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .formLogin() + .usernameParameter("custom-username") + .and() + .formLogin(); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void loginWhenInvalidLoginAndFailureForwardUrlThenForwardsToFailureForwardUrl() throws Exception { + this.spring.register(FormLoginUserForwardAuthenticationSuccessAndFailureConfig.class).autowire(); + + this.mockMvc.perform(formLogin().user("invalid")) + .andExpect(forwardedUrl("/failure_forward_url")); + } + + @Test + public void loginWhenSuccessForwardUrlThenForwardsToSuccessForwardUrl() throws Exception { + this.spring.register(FormLoginUserForwardAuthenticationSuccessAndFailureConfig.class).autowire(); + + this.mockMvc.perform(formLogin()) + .andExpect(forwardedUrl("/success_forward_url")); + } + + @EnableWebSecurity + static class FormLoginUserForwardAuthenticationSuccessAndFailureConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .csrf() + .disable() + .authorizeRequests() + .anyRequest().authenticated() + .and() + .formLogin() + .failureForwardUrl("/failure_forward_url") + .successForwardUrl("/success_forward_url") + .permitAll(); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void configureWhenRegisteringObjectPostProcessorThenInvokedOnUsernamePasswordAuthenticationFilter() { + ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class); + this.spring.register(ObjectPostProcessorConfig.class).autowire(); + + verify(ObjectPostProcessorConfig.objectPostProcessor) + .postProcess(any(UsernamePasswordAuthenticationFilter.class)); + } + + @Test + public void configureWhenRegisteringObjectPostProcessorThenInvokedOnLoginUrlAuthenticationEntryPoint() { + ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class); + this.spring.register(ObjectPostProcessorConfig.class).autowire(); + + verify(ObjectPostProcessorConfig.objectPostProcessor) + .postProcess(any(LoginUrlAuthenticationEntryPoint.class)); + } + + @Test + public void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() { + ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class); + this.spring.register(ObjectPostProcessorConfig.class).autowire(); + + verify(ObjectPostProcessorConfig.objectPostProcessor) + .postProcess(any(ExceptionTranslationFilter.class)); + } + + @EnableWebSecurity + static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter { + static ObjectPostProcessor objectPostProcessor; + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .exceptionHandling() + .and() + .formLogin(); + // @formatter:on + } + + @Bean + static ObjectPostProcessor objectPostProcessor() { + return objectPostProcessor; + } + } + + static class ReflectingObjectPostProcessor implements ObjectPostProcessor { + @Override + public O postProcess(O object) { + return object; + } + } }