浏览代码

Migrate LogoutConfigurerTests groovy->java

Issue: gh-4939
Eleftheria Stein 6 年之前
父节点
当前提交
e15922322e

+ 0 - 270
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy

@@ -1,270 +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.http.HttpStatus
-import org.springframework.http.MediaType
-import org.springframework.security.config.annotation.AnyObjectPostProcessor
-import org.springframework.security.config.annotation.BaseSpringSpec
-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.authentication.RememberMeServices
-import org.springframework.security.web.authentication.logout.LogoutFilter
-import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
-import org.springframework.security.web.util.matcher.RequestMatcher
-
-/**
- *
- * @author Rob Winch
- */
-class LogoutConfigurerTests extends BaseSpringSpec {
-
-	def defaultLogoutSuccessHandlerForNullLogoutHandler() {
-		setup:
-		LogoutConfigurer config = new LogoutConfigurer();
-		when:
-		config.defaultLogoutSuccessHandlerFor(null, Mock(RequestMatcher))
-		then:
-		thrown(IllegalArgumentException)
-	}
-
-	def defaultLogoutSuccessHandlerForNullMatcher() {
-		setup:
-		LogoutConfigurer config = new LogoutConfigurer();
-		when:
-		config.defaultLogoutSuccessHandlerFor(Mock(LogoutSuccessHandler), null)
-		then:
-		thrown(IllegalArgumentException)
-	}
-
-	def "logout ObjectPostProcessor"() {
-		setup:
-			AnyObjectPostProcessor opp = Mock()
-			HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
-		when:
-			http
-				.logout()
-					.and()
-				.build()
-
-		then: "LogoutFilter is registered with LifecycleManager"
-			1 * opp.postProcess(_ as LogoutFilter) >> {LogoutFilter o -> o}
-	}
-
-	def "invoke logout twice does not override"() {
-		when:
-			loadConfig(InvokeTwiceDoesNotOverride)
-			request.method = "POST"
-			request.servletPath = "/custom/logout"
-			findFilter(LogoutFilter).doFilter(request,response,chain)
-		then:
-			response.redirectedUrl == "/login?logout"
-	}
-
-	@EnableWebSecurity
-	static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.logout()
-					.logoutUrl("/custom/logout")
-					.and()
-				.logout()
-		}
-	}
-
-	def "Logout allows other methods if CSRF is disabled"() {
-		when:
-			loadConfig(CsrfDisabledConfig)
-			request.method = method
-			request.servletPath = "/logout"
-			findFilter(LogoutFilter).doFilter(request,response,chain)
-		then:
-			response.status == httpStatus.value()
-			response.redirectedUrl == url
-		where:
-			method    | httpStatus       | url
-			"GET"     | HttpStatus.FOUND | "/login?logout"
-			"POST"    | HttpStatus.FOUND | "/login?logout"
-			"PUT"     | HttpStatus.FOUND | "/login?logout"
-			"DELETE"  | HttpStatus.FOUND | "/login?logout"
-			"OPTIONS" | HttpStatus.OK    | null
-			"HEAD"    | HttpStatus.OK    | null
-			"TRACE"   | HttpStatus.OK    | null
-
-	}
-
-	@EnableWebSecurity
-	static class CsrfDisabledConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.csrf().disable()
-				.logout()
-		}
-	}
-
-
-	def "Logout allows other methods if CSRF is disabled with custom logout URL"() {
-		when:
-			loadConfig(CsrfDisabledCustomLogoutUrlConfig)
-			request.method = method
-			request.servletPath = "/custom/logout"
-			findFilter(LogoutFilter).doFilter(request,response,chain)
-		then:
-			response.status == httpStatus.value()
-			response.redirectedUrl == url
-		where:
-			method    | httpStatus       | url
-			"GET"     | HttpStatus.FOUND | "/login?logout"
-			"POST"    | HttpStatus.FOUND | "/login?logout"
-			"PUT"     | HttpStatus.FOUND | "/login?logout"
-			"DELETE"  | HttpStatus.FOUND | "/login?logout"
-			"OPTIONS" | HttpStatus.OK    | null
-			"HEAD"    | HttpStatus.OK    | null
-			"TRACE"   | HttpStatus.OK    | null
-
-	}
-
-	@EnableWebSecurity
-	static class CsrfDisabledCustomLogoutUrlConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.logout()
-					.logoutUrl("/custom/logout")
-					.and()
-				.csrf().disable()
-		}
-	}
-
-	def "SEC-3170: LogoutConfigurer RememberMeService not LogoutHandler"() {
-		setup:
-			RememberMeNoLogoutHandler.REMEMBER_ME = Mock(RememberMeServices)
-			loadConfig(RememberMeNoLogoutHandler)
-			request.method = "POST"
-			request.servletPath = "/logout"
-		when:
-			findFilter(LogoutFilter).doFilter(request,response,chain)
-		then:
-			response.redirectedUrl == "/login?logout"
-	}
-
-	def "SEC-3170: LogoutConfigurer prevents null LogoutHandler"() {
-		when:
-			new LogoutConfigurer().addLogoutHandler(null)
-		then:
-			thrown(IllegalArgumentException)
-	}
-
-	@EnableWebSecurity
-	static class RememberMeNoLogoutHandler extends WebSecurityConfigurerAdapter {
-		static RememberMeServices REMEMBER_ME
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.rememberMe()
-					.rememberMeServices(REMEMBER_ME)
-		}
-	}
-
-	def "LogoutConfigurer content negotiation text/html redirects"() {
-		setup:
-			loadConfig(LogoutHandlerContentNegotiation)
-		when:
-			login()
-			request.method = 'POST'
-			request.servletPath = '/logout'
-			request.addHeader('Accept', 'text/html')
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.status == 302
-			response.redirectedUrl == '/login?logout'
-	}
-
-	// gh-3282
-	def "LogoutConfigurer content negotiation json 201"() {
-		setup:
-			loadConfig(LogoutHandlerContentNegotiation)
-		when:
-			login()
-			request.method = 'POST'
-			request.servletPath = '/logout'
-			request.addHeader('Accept', 'application/json')
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.status == 204
-	}
-
-	// gh-4831
-	def "LogoutConfigurer content negotiation all 201"() {
-		setup:
-		loadConfig(LogoutHandlerContentNegotiation)
-		when:
-		login()
-		request.method = 'POST'
-		request.servletPath = '/logout'
-		request.addHeader('Accept', MediaType.ALL_VALUE)
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == 204
-	}
-
-	@EnableWebSecurity
-	static class LogoutHandlerContentNegotiation extends WebSecurityConfigurerAdapter {
-	}
-	// gh-3902
-	def "logout in chrome is 302"() {
-		setup:
-		loadConfig(LogoutHandlerContentNegotiationForChrome)
-		when:
-		login()
-		request.method = 'POST'
-		request.servletPath = '/logout'
-		request.addHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == 302
-	}
-
-	@EnableWebSecurity
-	static class LogoutHandlerContentNegotiationForChrome extends WebSecurityConfigurerAdapter {
-	}
-
-	// gh-3997
-	def "LogoutConfigurer for XMLHttpRequest is 204"() {
-		setup:
-			loadConfig(LogoutXMLHttpRequestConfig)
-		when:
-			login()
-			request.method = 'POST'
-			request.servletPath = '/logout'
-			request.addHeader('Accept', 'text/html,application/json')
-			request.addHeader('X-Requested-With', 'XMLHttpRequest')
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.status == 204
-	}
-
-	@EnableWebSecurity
-	static class LogoutXMLHttpRequestConfig extends WebSecurityConfigurerAdapter {
-	}
-}

+ 375 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.java

@@ -0,0 +1,375 @@
+/*
+ * 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.
+ * 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.apache.http.HttpHeaders;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.MediaType;
+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.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.security.web.authentication.logout.LogoutFilter;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Tests for {@link LogoutConfigurer}
+ *
+ * @author Rob Winch
+ * @author Eleftheria Stein
+ */
+public class LogoutConfigurerTests {
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	MockMvc mvc;
+
+	@Test
+	public void configureWhenDefaultLogoutSuccessHandlerForHasNullLogoutHandlerThenException() {
+		assertThatThrownBy(() -> this.spring.register(NullLogoutSuccessHandlerConfig.class).autowire())
+				.isInstanceOf(BeanCreationException.class)
+				.hasRootCauseInstanceOf(IllegalArgumentException.class);
+	}
+
+	@EnableWebSecurity
+	static class NullLogoutSuccessHandlerConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.logout()
+					.defaultLogoutSuccessHandlerFor(null, mock(RequestMatcher.class));
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void configureWhenDefaultLogoutSuccessHandlerForHasNullMatcherThenException() {
+		assertThatThrownBy(() -> this.spring.register(NullMatcherConfig.class).autowire())
+				.isInstanceOf(BeanCreationException.class)
+				.hasRootCauseInstanceOf(IllegalArgumentException.class);
+	}
+
+	@EnableWebSecurity
+	static class NullMatcherConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.logout()
+					.defaultLogoutSuccessHandlerFor(mock(LogoutSuccessHandler.class), null);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void configureWhenRegisteringObjectPostProcessorThenInvokedOnLogoutFilter() {
+		this.spring.register(ObjectPostProcessorConfig.class).autowire();
+
+		verify(ObjectPostProcessorConfig.objectPostProcessor)
+				.postProcess(any(LogoutFilter.class));
+	}
+
+	@EnableWebSecurity
+	static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
+		static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.logout();
+			// @formatter:on
+		}
+
+		@Bean
+		static ObjectPostProcessor<Object> objectPostProcessor() {
+			return objectPostProcessor;
+		}
+	}
+
+	static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
+		@Override
+		public <O> O postProcess(O object) {
+			return object;
+		}
+	}
+
+	@Test
+	public void logoutWhenInvokedTwiceThenUsesOriginalLogoutUrl() throws Exception {
+		this.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();
+
+		this.mvc.perform(post("/custom/logout")
+				.with(csrf()))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@EnableWebSecurity
+	static class DuplicateDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.logout()
+					.logoutUrl("/custom/logout")
+					.and()
+				.logout();
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication();
+			// @formatter:on
+		}
+	}
+
+	// SEC-2311
+	@Test
+	public void logoutWhenGetRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledConfig.class).autowire();
+
+		this.mvc.perform(get("/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenPostRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledConfig.class).autowire();
+
+		this.mvc.perform(post("/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenPutRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledConfig.class).autowire();
+
+		this.mvc.perform(put("/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenDeleteRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledConfig.class).autowire();
+
+		this.mvc.perform(delete("/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@EnableWebSecurity
+	static class CsrfDisabledConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.disable()
+				.logout();
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void logoutWhenGetRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();
+
+		this.mvc.perform(get("/custom/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenPostRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();
+
+		this.mvc.perform(post("/custom/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenPutRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();
+
+		this.mvc.perform(put("/custom/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@Test
+	public void logoutWhenDeleteRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {
+		this.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();
+
+		this.mvc.perform(delete("/custom/logout"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@EnableWebSecurity
+	static class CsrfDisabledAndCustomLogoutConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.disable()
+				.logout()
+					.logoutUrl("/custom/logout");
+			// @formatter:on
+		}
+	}
+
+	// SEC-3170
+	@Test
+	public void configureWhenLogoutHandlerNullThenException() {
+		assertThatThrownBy(() -> this.spring.register(NullLogoutHandlerConfig.class).autowire())
+				.isInstanceOf(BeanCreationException.class)
+				.hasRootCauseInstanceOf(IllegalArgumentException.class);
+	}
+
+	@EnableWebSecurity
+	static class NullLogoutHandlerConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.logout()
+					.addLogoutHandler(null);
+			// @formatter:on
+		}
+	}
+
+	// SEC-3170
+	@Test
+	public void rememberMeWhenRememberMeServicesNotLogoutHandlerThenRedirectsToLogin() throws Exception {
+		this.spring.register(RememberMeNoLogoutHandler.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf()))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	@EnableWebSecurity
+	static class RememberMeNoLogoutHandler extends WebSecurityConfigurerAdapter {
+		static RememberMeServices REMEMBER_ME = mock(RememberMeServices.class);
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.rememberMe()
+					.rememberMeServices(REMEMBER_ME);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void logoutWhenAcceptTextHtmlThenRedirectsToLogin() throws Exception {
+		this.spring.register(BasicSecurityConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf())
+				.with(user("user"))
+				.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	// gh-3282
+	@Test
+	public void logoutWhenAcceptApplicationJsonThenReturnsStatusNoContent() throws Exception {
+		this.spring.register(BasicSecurityConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf())
+				.with(user("user"))
+				.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE))
+				.andExpect(status().isNoContent());
+	}
+
+	// gh-4831
+	@Test
+	public void logoutWhenAcceptAllThenReturnsStatusNoContent() throws Exception {
+		this.spring.register(BasicSecurityConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf())
+				.with(user("user"))
+				.header(HttpHeaders.ACCEPT, MediaType.ALL_VALUE))
+				.andExpect(status().isNoContent());
+	}
+
+	// gh-3902
+	@Test
+	public void logoutWhenAcceptFromChromeThenRedirectsToLogin() throws Exception {
+		this.spring.register(BasicSecurityConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf()).with(user("user"))
+				.header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/login?logout"));
+	}
+
+	// gh-3997
+	@Test
+	public void logoutWhenXMLHttpRequestThenReturnsStatusNoContent() throws Exception {
+		this.spring.register(BasicSecurityConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf())
+				.with(user("user"))
+				.header(HttpHeaders.ACCEPT, "text/html,application/json")
+				.header("X-Requested-With", "XMLHttpRequest"))
+				.andExpect(status().isNoContent());
+	}
+
+	@EnableWebSecurity
+	static class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
+	}
+}