-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I went through everything to get it to fit with Spring's docuemntation standard. Lots of small changes for punctuation, grammar, usage, voice, and so on. Also added some links, mostly to the API Javadoc.
- Loading branch information
Jay Bryant
committed
Jun 4, 2021
1 parent
52b8202
commit 9219cbf
Showing
103 changed files
with
29,230 additions
and
5,045 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
271 changes: 271 additions & 0 deletions
271
0002-Make-Csrf-cookie-secure-flag-configurable-WebFlux.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
From e2993d93e109c1a3c9020b7ea9efb6e556751ed4 Mon Sep 17 00:00:00 2001 | ||
This comment has been minimized.
Sorry, something went wrong. |
||
From: Thomas Vitale <[email protected]> | ||
Date: Mon, 26 Apr 2021 18:13:20 +0200 | ||
Subject: [PATCH 2/3] Make Csrf cookie secure flag configurable (WebFlux) | ||
|
||
Make the XSRF-TOKEN cookie secure flag configurable in CookieServerCsrfTokenRepository. | ||
|
||
Closes gh-9678 | ||
--- | ||
.../csrf/CookieServerCsrfTokenRepository.java | 30 ++++-- | ||
.../CookieServerCsrfTokenRepositoryTests.java | 100 ++++++++++++++++-- | ||
2 files changed, 113 insertions(+), 17 deletions(-) | ||
|
||
diff --git a/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java b/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java | ||
index 5910ff3e45..bc3a20e711 100644 | ||
--- a/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java | ||
+++ b/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java | ||
@@ -1,5 +1,5 @@ | ||
/* | ||
- * Copyright 2002-2019 the original author or authors. | ||
+ * Copyright 2002-2021 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. | ||
@@ -34,6 +34,7 @@ import org.springframework.web.server.ServerWebExchange; | ||
* AngularJS. When using with AngularJS be sure to use {@link #withHttpOnlyFalse()} . | ||
* | ||
* @author Eric Deandrea | ||
+ * @author Thomas Vitale | ||
* @since 5.1 | ||
*/ | ||
public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRepository { | ||
@@ -54,6 +55,8 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep | ||
|
||
private boolean cookieHttpOnly = true; | ||
|
||
+ private Boolean secure; | ||
+ | ||
/** | ||
* Factory method to conveniently create an instance that has | ||
* {@link #setCookieHttpOnly(boolean)} set to false. | ||
@@ -75,11 +78,16 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep | ||
public Mono<Void> saveToken(ServerWebExchange exchange, CsrfToken token) { | ||
return Mono.fromRunnable(() -> { | ||
String tokenValue = (token != null) ? token.getToken() : ""; | ||
- int maxAge = !tokenValue.isEmpty() ? -1 : 0; | ||
- String path = (this.cookiePath != null) ? this.cookiePath : getRequestContext(exchange.getRequest()); | ||
- boolean secure = exchange.getRequest().getSslInfo() != null; | ||
- ResponseCookie cookie = ResponseCookie.from(this.cookieName, tokenValue).domain(this.cookieDomain) | ||
- .httpOnly(this.cookieHttpOnly).maxAge(maxAge).path(path).secure(secure).build(); | ||
+ // @formatter:off | ||
+ ResponseCookie cookie = ResponseCookie | ||
+ .from(this.cookieName, tokenValue) | ||
+ .domain(this.cookieDomain) | ||
+ .httpOnly(this.cookieHttpOnly) | ||
+ .maxAge(!tokenValue.isEmpty() ? -1 : 0) | ||
+ .path((this.cookiePath != null) ? this.cookiePath : getRequestContext(exchange.getRequest())) | ||
+ .secure((this.secure != null) ? this.secure : (exchange.getRequest().getSslInfo() != null)) | ||
+ .build(); | ||
+ // @formatter:on | ||
exchange.getResponse().addCookie(cookie); | ||
}); | ||
} | ||
@@ -146,6 +154,16 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep | ||
this.cookieDomain = cookieDomain; | ||
} | ||
|
||
+ /** | ||
+ * Sets the cookie secure flag. If not set, the value depends on | ||
+ * {@link ServerHttpRequest#getSslInfo()}. | ||
+ * @param secure The value for the secure flag | ||
+ * @since 5.5 | ||
+ */ | ||
+ public void setSecure(boolean secure) { | ||
+ this.secure = secure; | ||
+ } | ||
+ | ||
private CsrfToken createCsrfToken() { | ||
return createCsrfToken(createNewToken()); | ||
} | ||
diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java | ||
index d16f131920..7160337053 100644 | ||
--- a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java | ||
+++ b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java | ||
@@ -1,5 +1,5 @@ | ||
/* | ||
- * Copyright 2002-2018 the original author or authors. | ||
+ * Copyright 2002-2021 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. | ||
@@ -16,12 +16,15 @@ | ||
|
||
package org.springframework.security.web.server.csrf; | ||
|
||
+import java.security.cert.X509Certificate; | ||
import java.time.Duration; | ||
|
||
+import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import org.springframework.http.HttpCookie; | ||
import org.springframework.http.ResponseCookie; | ||
+import org.springframework.http.server.reactive.SslInfo; | ||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; | ||
import org.springframework.mock.web.server.MockServerWebExchange; | ||
import org.springframework.util.StringUtils; | ||
@@ -30,13 +33,14 @@ import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
/** | ||
* @author Eric Deandrea | ||
+ * @author Thomas Vitale | ||
* @since 5.1 | ||
*/ | ||
public class CookieServerCsrfTokenRepositoryTests { | ||
|
||
- private MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/someUri")); | ||
+ private CookieServerCsrfTokenRepository csrfTokenRepository; | ||
|
||
- private CookieServerCsrfTokenRepository csrfTokenRepository = new CookieServerCsrfTokenRepository(); | ||
+ private MockServerHttpRequest.BaseBuilder<?> request; | ||
|
||
private String expectedHeaderName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME; | ||
|
||
@@ -56,6 +60,12 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
|
||
private String expectedCookieValue = "csrfToken"; | ||
|
||
+ @Before | ||
+ public void setUp() { | ||
+ this.csrfTokenRepository = new CookieServerCsrfTokenRepository(); | ||
+ this.request = MockServerHttpRequest.get("/someUri"); | ||
+ } | ||
+ | ||
@Test | ||
public void generateTokenWhenDefaultThenDefaults() { | ||
generateTokenAndAssertExpectedValues(); | ||
@@ -82,8 +92,9 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
|
||
@Test | ||
public void saveTokenWhenNoSubscriptionThenNotWritten() { | ||
- this.csrfTokenRepository.saveToken(this.exchange, createToken()); | ||
- assertThat(this.exchange.getResponse().getCookies().getFirst(this.expectedCookieName)).isNull(); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()); | ||
+ assertThat(exchange.getResponse().getCookies().getFirst(this.expectedCookieName)).isNull(); | ||
} | ||
|
||
@Test | ||
@@ -112,6 +123,56 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
saveAndAssertExpectedValues(createToken()); | ||
} | ||
|
||
+ @Test | ||
+ public void saveTokenWhenSslInfoPresentThenSecure() { | ||
+ this.request.sslInfo(new MockSslInfo()); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ assertThat(cookie).isNotNull(); | ||
+ assertThat(cookie.isSecure()).isTrue(); | ||
+ } | ||
+ | ||
+ @Test | ||
+ public void saveTokenWhenSslInfoNullThenNotSecure() { | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ assertThat(cookie).isNotNull(); | ||
+ assertThat(cookie.isSecure()).isFalse(); | ||
+ } | ||
+ | ||
+ @Test | ||
+ public void saveTokenWhenSecureFlagTrueThenSecure() { | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.setSecure(true); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ assertThat(cookie).isNotNull(); | ||
+ assertThat(cookie.isSecure()).isTrue(); | ||
+ } | ||
+ | ||
+ @Test | ||
+ public void saveTokenWhenSecureFlagFalseThenNotSecure() { | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.setSecure(false); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ assertThat(cookie).isNotNull(); | ||
+ assertThat(cookie.isSecure()).isFalse(); | ||
+ } | ||
+ | ||
+ @Test | ||
+ public void saveTokenWhenSecureFlagFalseAndSslInfoThenNotSecure() { | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.request.sslInfo(new MockSslInfo()); | ||
+ this.csrfTokenRepository.setSecure(false); | ||
+ this.csrfTokenRepository.saveToken(exchange, createToken()).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ assertThat(cookie).isNotNull(); | ||
+ assertThat(cookie.isSecure()).isFalse(); | ||
+ } | ||
+ | ||
@Test | ||
public void loadTokenWhenCookieExistThenTokenFound() { | ||
loadAndAssertExpectedValues(); | ||
@@ -127,7 +188,8 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
|
||
@Test | ||
public void loadTokenWhenNoCookiesThenNullToken() { | ||
- CsrfToken csrfToken = this.csrfTokenRepository.loadToken(this.exchange).block(); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ CsrfToken csrfToken = this.csrfTokenRepository.loadToken(exchange).block(); | ||
assertThat(csrfToken).isNull(); | ||
} | ||
|
||
@@ -180,8 +242,8 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
private void loadAndAssertExpectedValues() { | ||
MockServerHttpRequest.BodyBuilder request = MockServerHttpRequest.post("/someUri") | ||
.cookie(new HttpCookie(this.expectedCookieName, this.expectedCookieValue)); | ||
- this.exchange = MockServerWebExchange.from(request); | ||
- CsrfToken csrfToken = this.csrfTokenRepository.loadToken(this.exchange).block(); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(request); | ||
+ CsrfToken csrfToken = this.csrfTokenRepository.loadToken(exchange).block(); | ||
if (StringUtils.hasText(this.expectedCookieValue)) { | ||
assertThat(csrfToken).isNotNull(); | ||
assertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName); | ||
@@ -198,8 +260,9 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
this.expectedMaxAge = Duration.ofSeconds(0); | ||
this.expectedCookieValue = ""; | ||
} | ||
- this.csrfTokenRepository.saveToken(this.exchange, token).block(); | ||
- ResponseCookie cookie = this.exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ this.csrfTokenRepository.saveToken(exchange, token).block(); | ||
+ ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName); | ||
assertThat(cookie).isNotNull(); | ||
assertThat(cookie.getMaxAge()).isEqualTo(this.expectedMaxAge); | ||
assertThat(cookie.getDomain()).isEqualTo(this.expectedDomain); | ||
@@ -211,7 +274,8 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
} | ||
|
||
private void generateTokenAndAssertExpectedValues() { | ||
- CsrfToken csrfToken = this.csrfTokenRepository.generateToken(this.exchange).block(); | ||
+ MockServerWebExchange exchange = MockServerWebExchange.from(this.request); | ||
+ CsrfToken csrfToken = this.csrfTokenRepository.generateToken(exchange).block(); | ||
assertThat(csrfToken).isNotNull(); | ||
assertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName); | ||
assertThat(csrfToken.getParameterName()).isEqualTo(this.expectedParameterName); | ||
@@ -226,4 +290,18 @@ public class CookieServerCsrfTokenRepositoryTests { | ||
return new DefaultCsrfToken(headerName, parameterName, tokenValue); | ||
} | ||
|
||
+ static class MockSslInfo implements SslInfo { | ||
+ | ||
+ @Override | ||
+ public String getSessionId() { | ||
+ return "sessionId"; | ||
+ } | ||
+ | ||
+ @Override | ||
+ public X509Certificate[] getPeerCertificates() { | ||
+ return new X509Certificate[] {}; | ||
+ } | ||
+ | ||
+ } | ||
+ | ||
} | ||
-- | ||
2.24.1 | ||
|
Oops, something went wrong.
#13621