Преглед на файлове

Polish CookieServerCsrfTokenRepository

- Only do work if subscribed to
- use test naming conventions
- Refactor tests to avoid extracting
  - Uses String for member names which are not type safe
  - Uses long argument list which makes assertions difficult to read

Issue: gh-5083
Rob Winch преди 7 години
родител
ревизия
3ba15a16bf

+ 20 - 22
web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java

@@ -21,8 +21,6 @@ import java.util.UUID;
 
 import org.springframework.http.HttpCookie;
 import org.springframework.http.ResponseCookie;
-import org.springframework.http.server.PathContainer;
-import org.springframework.http.server.RequestPath;
 import org.springframework.http.server.reactive.ServerHttpRequest;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
@@ -70,30 +68,30 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep
 
 	@Override
 	public Mono<Void> saveToken(ServerWebExchange exchange, CsrfToken token) {
-		Optional<String> tokenValue = Optional.ofNullable(token).map(CsrfToken::getToken);
-
-		ResponseCookie cookie = ResponseCookie.from(this.cookieName, tokenValue.orElse(""))
-			.domain(this.cookieDomain)
-			.httpOnly(this.cookieHttpOnly)
-			.maxAge(tokenValue.map(val -> -1).orElse(0))
-			.path(Optional.ofNullable(this.cookiePath).orElseGet(() -> getRequestContext(exchange.getRequest())))
-			.secure(Optional.ofNullable(exchange.getRequest().getSslInfo()).map(sslInfo -> true).orElse(false))
-			.build();
-
-		exchange.getResponse().addCookie(cookie);
-
-		return Mono.empty();
+		return Mono.fromRunnable(() -> {
+			Optional<String> tokenValue = Optional.ofNullable(token).map(CsrfToken::getToken);
+
+			ResponseCookie cookie = ResponseCookie.from(this.cookieName, tokenValue.orElse(""))
+					.domain(this.cookieDomain)
+					.httpOnly(this.cookieHttpOnly)
+					.maxAge(tokenValue.map(val -> -1).orElse(0))
+					.path(Optional.ofNullable(this.cookiePath).orElseGet(() -> getRequestContext(exchange.getRequest())))
+					.secure(Optional.ofNullable(exchange.getRequest().getSslInfo()).map(sslInfo -> true).orElse(false))
+					.build();
+
+			exchange.getResponse().addCookie(cookie);
+		});
 	}
 
 	@Override
 	public Mono<CsrfToken> loadToken(ServerWebExchange exchange) {
-		Optional<CsrfToken> token = Optional.ofNullable(exchange.getRequest())
-			.map(ServerHttpRequest::getCookies)
-			.map(cookiesMap -> cookiesMap.getFirst(this.cookieName))
-			.map(HttpCookie::getValue)
-			.map(this::createCsrfToken);
-
-		return Mono.justOrEmpty(token);
+		return Mono.fromCallable(() -> {
+			HttpCookie csrfCookie = exchange.getRequest().getCookies().getFirst(this.cookieName);
+			if (csrfCookie == null) {
+				return null;
+			}
+			return createCsrfToken(csrfCookie.getValue());
+		});
 	}
 
 	/**

+ 145 - 173
web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java

@@ -19,236 +19,208 @@ package org.springframework.security.web.server.csrf;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.time.Duration;
-import java.util.function.Supplier;
 
 import org.junit.Test;
-
+import org.springframework.http.HttpCookie;
 import org.springframework.http.ResponseCookie;
 import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
 import org.springframework.mock.web.server.MockServerWebExchange;
 
-import reactor.core.publisher.Mono;
-
 /**
  * @author Eric Deandrea
  * @since 5.1
  */
 public class CookieServerCsrfTokenRepositoryTests {
-	@Test
-	public void generateTokenDefault() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new);
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.generateToken(exchange);
+	private MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block())
-			.isNotNull()
-			.extracting("headerName", "parameterName")
-			.containsExactly(CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME, CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME);
-		assertThat(csrfTokenMono.block().getToken()).isNotBlank();
-	}
+	private CookieServerCsrfTokenRepository csrfTokenRepository = new CookieServerCsrfTokenRepository();
+
+	private String expectedHeaderName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME;
+
+	private String expectedParameterName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME;
+
+	private Duration expectedMaxAge = Duration.ofSeconds(-1);
+
+	private String expectedDomain = null;
+
+	private String expectedPath = "/";
+
+	private boolean expectedSecure = false;
+
+	private boolean expectedHttpOnly = true;
+
+	private String expectedCookieName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME;
+
+	private String expectedCookieValue = "csrfToken";
 
 	@Test
-	public void generateTokenChangeHeaderName() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME,
-				"someHeader",
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME);
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.generateToken(exchange);
-
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block())
-			.isNotNull()
-			.extracting("headerName", "parameterName")
-			.containsExactly("someHeader", CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME);
-		assertThat(csrfTokenMono.block().getToken()).isNotBlank();
+	public void generateTokenWhenDefaultThenDefaults() {
+		generateTokenAndAssertExpectedValues();
 	}
 
 	@Test
-	public void generateTokenChangeParameterName() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME,
-				"someParam");
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.generateToken(exchange);
-
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block())
-			.isNotNull()
-			.extracting("headerName", "parameterName")
-			.containsExactly(CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME, "someParam");
-		assertThat(csrfTokenMono.block().getToken()).isNotBlank();
+	public void generateTokenWhenCustomHeaderThenCustomHeader() {
+		setExpectedHeaderName("someHeader");
+
+		generateTokenAndAssertExpectedValues();
 	}
 
 	@Test
-	public void generateTokenChangeHeaderAndParameterName() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME,
-				"someHeader",
-				"someParam");
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.generateToken(exchange);
-
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block())
-			.isNotNull()
-			.extracting("headerName", "parameterName")
-			.containsExactly("someHeader", "someParam");
-		assertThat(csrfTokenMono.block().getToken()).isNotBlank();
+	public void generateTokenWhenCustomParameterThenCustomParameter() {
+		setExpectedParameterName("someParam");
+
+		generateTokenAndAssertExpectedValues();
 	}
 
 	@Test
-	public void saveTokenDefault() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new);
-
-		Mono<Void> csrfTokenMono = csrfTokenRepository.saveToken(exchange, createToken("someTokenValue"));
-		ResponseCookie cookie = exchange
-			.getResponse()
-			.getCookies()
-			.getFirst(CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);
+	public void generateTokenWhenCustomHeaderAndParameterThenCustomHeaderAndParameter() {
+		setExpectedHeaderName("someHeader");
+		setExpectedParameterName("someParam");
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(cookie)
-			.isNotNull()
-			.extracting("maxAge", "domain", "path", "secure", "httpOnly", "name", "value")
-			.containsExactly(Duration.ofSeconds(-1), null, "/", false, true, "XSRF-TOKEN", "someTokenValue");
+		generateTokenAndAssertExpectedValues();
 	}
 
 	@Test
-	public void saveTokenMaxAge() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new);
+	public void saveTokenWhenNoSubscriptionThenNotWritten() {
+		this.csrfTokenRepository.saveToken(this.exchange, createToken());
 
-		Mono<Void> csrfTokenMono = csrfTokenRepository.saveToken(exchange, null);
-		ResponseCookie cookie = exchange
-			.getResponse()
-			.getCookies()
-			.getFirst(CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);
+		assertThat(this.exchange
+				.getResponse()
+				.getCookies()
+				.getFirst(this.expectedCookieName)).isNull();
+	}
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(cookie)
-			.isNotNull()
-			.extracting("maxAge", "domain", "path", "secure", "httpOnly", "name", "value")
-			.containsExactly(Duration.ofSeconds(0), null, "/", false, true, "XSRF-TOKEN", "");
+	@Test
+	public void saveTokenWhenDefaultThenDefaults() {
+		saveAndAssertExpectedValues(createToken());
 	}
 
 	@Test
-	public void saveTokenHttpOnly() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::withHttpOnlyFalse);
+	public void saveTokenWhenNullThenDeletes() {
+		saveAndAssertExpectedValues(null);
+	}
 
-		Mono<Void> csrfTokenMono = csrfTokenRepository.saveToken(exchange, createToken("someTokenValue"));
-		ResponseCookie cookie = exchange
-			.getResponse()
-			.getCookies()
-			.getFirst(CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);
+	@Test
+	public void saveTokenWhenHttpOnlyFalseThenHttpOnlyFalse() {
+		setExpectedHttpOnly(false);
+
+		saveAndAssertExpectedValues(createToken());
+	}
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(cookie)
-			.isNotNull()
-			.extracting("maxAge", "domain", "path", "secure", "httpOnly", "name", "value")
-			.containsExactly(Duration.ofSeconds(-1), null, "/", false, false, "XSRF-TOKEN", "someTokenValue");
+	@Test
+	public void saveTokenWhenCustomPropertiesThenCustomProperties() {
+		setExpectedDomain(".spring.io");
+		setExpectedCookieName("csrfCookie");
+		setExpectedPath("/some/path");
+		setExpectedHeaderName("headerName");
+		setExpectedParameterName("paramName");
+
+		saveAndAssertExpectedValues(createToken());
 	}
 
 	@Test
-	public void saveTokenOverriddenViaCsrfProps() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new,
-			".spring.io",  "csrfCookie", "/some/path",
-				"headerName", "paramName");
+	public void loadTokenWhenCookieExistThenTokenFound() {
+		loadAndAssertExpectedValues();
+	}
 
-		Mono<Void> csrfTokenMono =
-			csrfTokenRepository.saveToken(exchange, createToken("headerName", "paramName", "someTokenValue"));
-		ResponseCookie cookie = exchange.getResponse().getCookies().getFirst("csrfCookie");
+	@Test
+	public void loadTokenWhenCustomThenTokenFound() {
+		setExpectedParameterName("paramName");
+		setExpectedHeaderName("headerName");
+		setExpectedCookieName("csrfCookie");
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(cookie)
-			.isNotNull()
-			.extracting("maxAge", "domain", "path", "secure", "httpOnly", "name", "value")
-			.containsExactly(Duration.ofSeconds(-1), ".spring.io", "/some/path", false, true, "csrfCookie", "someTokenValue");
+		saveAndAssertExpectedValues(createToken());
 	}
 
 	@Test
-	public void loadTokenThatExists() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(
-			MockServerHttpRequest.post("/someUri")
-				.cookie(ResponseCookie.from(CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, "someTokenValue").build()));
+	public void loadTokenWhenNoCookiesThenNullToken() {
+		CsrfToken csrfToken = this.csrfTokenRepository.loadToken(this.exchange).block();
+		assertThat(csrfToken).isNull();
+	}
 
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new);
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.loadToken(exchange);
+	private void setExpectedHeaderName(String expectedHeaderName) {
+		this.csrfTokenRepository.setHeaderName(expectedHeaderName);
+		this.expectedHeaderName = expectedHeaderName;
+	}
 
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block())
-			.isNotNull()
-			.extracting("headerName", "parameterName", "token")
-			.containsExactly(
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME,
-				"someTokenValue");
+	private void setExpectedParameterName(String expectedParameterName) {
+		this.csrfTokenRepository.setParameterName(expectedParameterName);
+		this.expectedParameterName = expectedParameterName;
 	}
 
-	@Test
-	public void loadTokenThatDoesntExists() {
-		MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post("/someUri"));
-		CookieServerCsrfTokenRepository csrfTokenRepository =
-			CookieServerCsrfTokenRepositoryFactory.createRepository(CookieServerCsrfTokenRepository::new);
+	private void setExpectedDomain(String expectedDomain) {
+		this.csrfTokenRepository.setCookieDomain(expectedDomain);
+		this.expectedDomain = expectedDomain;
+	}
 
-		Mono<CsrfToken> csrfTokenMono = csrfTokenRepository.loadToken(exchange);
-		assertThat(csrfTokenMono).isNotNull();
-		assertThat(csrfTokenMono.block()).isNull();
+	private void setExpectedPath(String expectedPath) {
+		this.csrfTokenRepository.setCookiePath(expectedPath);
+		this.expectedPath = expectedPath;
 	}
 
-	private static CsrfToken createToken(String tokenValue) {
-		return createToken(CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME,
-			CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME, tokenValue);
+	private void setExpectedHttpOnly(boolean expectedHttpOnly) {
+		this.expectedHttpOnly = expectedHttpOnly;
+		this.csrfTokenRepository.setCookieHttpOnly(expectedHttpOnly);
 	}
 
-	private static CsrfToken createToken(String headerName, String parameterName, String tokenValue) {
-		return new DefaultCsrfToken(headerName, parameterName, tokenValue);
+	private void setExpectedCookieName(String expectedCookieName) {
+		this.expectedCookieName = expectedCookieName;
+		this.csrfTokenRepository.setCookieName(expectedCookieName);
 	}
 
-	static final class CookieServerCsrfTokenRepositoryFactory {
-		private CookieServerCsrfTokenRepositoryFactory() {
-			super();
-		}
+	private void loadAndAssertExpectedValues() {
+		MockServerHttpRequest.BodyBuilder request = MockServerHttpRequest.post("/someUri")
+				.cookie(new HttpCookie(this.expectedCookieName,
+						this.expectedCookieValue));
+		this.exchange = MockServerWebExchange.from(request);
 
-		static CookieServerCsrfTokenRepository createRepository(Supplier<CookieServerCsrfTokenRepository> cookieServerCsrfTokenRepositorySupplier) {
-			return createRepository(cookieServerCsrfTokenRepositorySupplier,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME,
-				CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME);
-		}
+		CsrfToken csrfToken = this.csrfTokenRepository.loadToken(this.exchange).block();
 
-		static CookieServerCsrfTokenRepository createRepository(
-			Supplier<CookieServerCsrfTokenRepository> cookieServerCsrfTokenRepositorySupplier,
-			String cookieName, String headerName, String parameterName) {
+		assertThat(csrfToken).isNotNull();
+		assertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName);
+		assertThat(csrfToken.getParameterName()).isEqualTo(this.expectedParameterName);
+		assertThat(csrfToken.getToken()).isEqualTo(this.expectedCookieValue);
+	}
 
-			return createRepository(cookieServerCsrfTokenRepositorySupplier,
-				null, cookieName, null, headerName, parameterName);
+	private void saveAndAssertExpectedValues(CsrfToken token) {
+		if (token == null) {
+			this.expectedMaxAge = Duration.ofSeconds(0);
+			this.expectedCookieValue = "";
 		}
 
-		static CookieServerCsrfTokenRepository createRepository(
-			Supplier<CookieServerCsrfTokenRepository> cookieServerCsrfTokenRepositorySupplier,
-			String cookieDomain, String cookieName, String cookiePath, String headerName, String parameterName) {
+		this.csrfTokenRepository.saveToken(this.exchange, token).block();
+
+		ResponseCookie cookie =  this.exchange
+				.getResponse()
+				.getCookies()
+				.getFirst(this.expectedCookieName);
+
+		assertThat(cookie).isNotNull();
+		assertThat(cookie.getMaxAge()).isEqualTo(this.expectedMaxAge);
+		assertThat(cookie.getDomain()).isEqualTo(this.expectedDomain);
+		assertThat(cookie.getPath()).isEqualTo(this.expectedPath);
+		assertThat(cookie.isSecure()).isEqualTo(this.expectedSecure);
+		assertThat(cookie.isHttpOnly()).isEqualTo(this.expectedHttpOnly);
+		assertThat(cookie.getName()).isEqualTo(this.expectedCookieName);
+		assertThat(cookie.getValue()).isEqualTo(this.expectedCookieValue);
+	}
 
-			return cookieServerCsrfTokenRepositorySupplier.get()
-				.withCookieDomain(cookieDomain)
-				.withCookieName(cookieName)
-				.withCookiePath(cookiePath)
-				.withHeaderName(headerName)
-				.withParameterName(parameterName);
-		}
+	private void generateTokenAndAssertExpectedValues() {
+		CsrfToken csrfToken = this.csrfTokenRepository.generateToken(this.exchange).block();
+
+		assertThat(csrfToken).isNotNull();
+		assertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName);
+		assertThat(csrfToken.getParameterName()).isEqualTo(this.expectedParameterName);
+		assertThat(csrfToken.getToken()).isNotBlank();
+	}
+
+
+	private CsrfToken createToken() {
+		return createToken(this.expectedHeaderName,
+			this.expectedParameterName, this.expectedCookieValue);
+	}
+
+	private static CsrfToken createToken(String headerName, String parameterName, String tokenValue) {
+		return new DefaultCsrfToken(headerName, parameterName, tokenValue);
 	}
-}
+}