|
@@ -33,9 +33,12 @@ import org.springframework.security.core.userdetails.UserDetails;
|
|
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
|
import org.springframework.security.test.web.CodecTestUtils;
|
|
|
+import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.RememberMeTokenAlgorithm;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
|
import static org.mockito.BDDMockito.given;
|
|
@@ -47,6 +50,7 @@ import static org.mockito.Mockito.mock;
|
|
|
* .
|
|
|
*
|
|
|
* @author Ben Alex
|
|
|
+ * @author Marcus Da Coregio
|
|
|
*/
|
|
|
public class TokenBasedRememberMeServicesTests {
|
|
|
|
|
@@ -77,8 +81,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
|
|
|
private long determineExpiryTimeFromBased64EncodedToken(String validToken) {
|
|
|
String cookieAsPlainText = CodecTestUtils.decodeBase64(validToken);
|
|
|
- String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
|
|
- if (cookieTokens.length == 3) {
|
|
|
+ String[] cookieTokens = getCookieTokens(cookieAsPlainText);
|
|
|
+ if (isValidCookieTokensLength(cookieTokens)) {
|
|
|
try {
|
|
|
return Long.parseLong(cookieTokens[1]);
|
|
|
}
|
|
@@ -88,15 +92,52 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- private String generateCorrectCookieContentForToken(long expiryTime, String username, String password, String key) {
|
|
|
+ private String[] getCookieTokens(String cookieAsPlainText) {
|
|
|
+ return StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
|
|
+ }
|
|
|
+
|
|
|
+ private String determineAlgorithmNameFromBase64EncodedToken(String validToken) {
|
|
|
+ String cookieAsPlainText = CodecTestUtils.decodeBase64(validToken);
|
|
|
+ String[] cookieTokens = getCookieTokens(cookieAsPlainText);
|
|
|
+ if (isValidCookieTokensLength(cookieTokens)) {
|
|
|
+ return cookieTokens[2];
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isValidCookieTokensLength(String[] cookieTokens) {
|
|
|
+ return cookieTokens.length == 3 || cookieTokens.length == 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateCorrectCookieContentForTokenNoAlgorithmName(long expiryTime, String username,
|
|
|
+ String password, String key) {
|
|
|
+ return generateCorrectCookieContentForTokenWithAlgorithmName(expiryTime, username, password, key,
|
|
|
+ RememberMeTokenAlgorithm.MD5);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateCorrectCookieContentForTokenNoAlgorithmName(long expiryTime, String username,
|
|
|
+ String password, String key, RememberMeTokenAlgorithm algorithm) {
|
|
|
// format is:
|
|
|
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" +
|
|
|
// password + ":" + key)
|
|
|
- String signatureValue = CodecTestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
|
|
|
+ String signatureValue = CodecTestUtils.algorithmHex(algorithm.getDigestAlgorithm(),
|
|
|
+ username + ":" + expiryTime + ":" + password + ":" + key);
|
|
|
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
|
|
|
return CodecTestUtils.encodeBase64(tokenValue);
|
|
|
}
|
|
|
|
|
|
+ private String generateCorrectCookieContentForTokenWithAlgorithmName(long expiryTime, String username,
|
|
|
+ String password, String key, RememberMeTokenAlgorithm algorithm) {
|
|
|
+ // format is:
|
|
|
+ // username + ":" + expiryTime + ":" + algorithmName + ":" + algorithmHex(username
|
|
|
+ // + ":" + expiryTime + ":" +
|
|
|
+ // password + ":" + key)
|
|
|
+ String signatureValue = CodecTestUtils.algorithmHex(algorithm.getDigestAlgorithm(),
|
|
|
+ username + ":" + expiryTime + ":" + password + ":" + key);
|
|
|
+ String tokenValue = username + ":" + expiryTime + ":" + algorithm.name() + ":" + signatureValue;
|
|
|
+ return CodecTestUtils.encodeBase64(tokenValue);
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
public void autoLoginReturnsNullIfNoCookiePresented() {
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -120,8 +161,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
@Test
|
|
|
public void autoLoginReturnsNullForExpiredCookieAndClearsCookie() {
|
|
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
- generateCorrectCookieContentForToken(System.currentTimeMillis() - 1000000, "someone", "password",
|
|
|
- "key"));
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() - 1000000, "someone",
|
|
|
+ "password", "key"));
|
|
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
request.setCookies(cookie);
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -161,8 +202,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
public void autoLoginClearsCookieIfSignatureBlocksDoesNotMatchExpectedValue() {
|
|
|
udsWillReturnUser();
|
|
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
- generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
|
|
- "WRONG_KEY"));
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "WRONG_KEY"));
|
|
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
request.setCookies(cookie);
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -189,8 +230,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
public void autoLoginClearsCookieIfUserNotFound() {
|
|
|
udsWillThrowNotFound();
|
|
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
- generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
|
|
- "key"));
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key"));
|
|
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
request.setCookies(cookie);
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -204,8 +245,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
public void autoLoginClearsCookieIfUserServiceMisconfigured() {
|
|
|
udsWillReturnNull();
|
|
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
- generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
|
|
- "key"));
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key"));
|
|
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
request.setCookies(cookie);
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -216,8 +257,8 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
public void autoLoginWithValidTokenAndUserSucceeds() {
|
|
|
udsWillReturnUser();
|
|
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
- generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
|
|
- "key"));
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key"));
|
|
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
request.setCookies(cookie);
|
|
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
@@ -226,6 +267,68 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
assertThat(result.getPrincipal()).isEqualTo(this.user);
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void autoLoginWhenTokenNoAlgorithmAndDifferentMatchingAlgorithmThenReturnsNullAndClearCookie() {
|
|
|
+ udsWillReturnUser();
|
|
|
+ Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key", RememberMeTokenAlgorithm.MD5));
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.setCookies(cookie);
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
|
|
|
+ Authentication result = this.services.autoLogin(request, response);
|
|
|
+ Cookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
|
|
+ assertThat(result).isNull();
|
|
|
+ assertThat(returnedCookie).isNotNull();
|
|
|
+ assertThat(returnedCookie.getMaxAge()).isZero();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void autoLoginWhenTokenNoAlgorithmAndSameMatchingAlgorithmThenSucceeds() {
|
|
|
+ udsWillReturnUser();
|
|
|
+ Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
+ generateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key", RememberMeTokenAlgorithm.SHA256));
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.setCookies(cookie);
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
|
|
|
+ Authentication result = this.services.autoLogin(request, response);
|
|
|
+ assertThat(result).isNotNull();
|
|
|
+ assertThat(result.getPrincipal()).isEqualTo(this.user);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void autoLoginWhenTokenHasAlgorithmAndSameMatchingAlgorithmThenUsesTokenAlgorithmAndSucceeds() {
|
|
|
+ udsWillReturnUser();
|
|
|
+ Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
+ generateCorrectCookieContentForTokenWithAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key", RememberMeTokenAlgorithm.SHA256));
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.setCookies(cookie);
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
|
|
|
+ Authentication result = this.services.autoLogin(request, response);
|
|
|
+ assertThat(result).isNotNull();
|
|
|
+ assertThat(result.getPrincipal()).isEqualTo(this.user);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void autoLoginWhenTokenHasAlgorithmAndDifferentMatchingAlgorithmThenUsesTokenAlgorithmAndSucceeds() {
|
|
|
+ udsWillReturnUser();
|
|
|
+ Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
|
|
+ generateCorrectCookieContentForTokenWithAlgorithmName(System.currentTimeMillis() + 1000000, "someone",
|
|
|
+ "password", "key", RememberMeTokenAlgorithm.SHA256));
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.setCookies(cookie);
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
|
|
|
+ Authentication result = this.services.autoLogin(request, response);
|
|
|
+ assertThat(result).isNotNull();
|
|
|
+ assertThat(result.getPrincipal()).isEqualTo(this.user);
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
public void testGettersSetters() {
|
|
|
assertThat(this.services.getUserDetailsService()).isEqualTo(this.uds);
|
|
@@ -293,6 +396,37 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void loginSuccessWhenDefaultEncodingAlgorithmThenContainsAlgorithmName() {
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, "true");
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services.loginSuccess(request, response,
|
|
|
+ new TestingAuthenticationToken("someone", "password", "ROLE_ABC"));
|
|
|
+ Cookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
|
|
+ assertThat(cookie).isNotNull();
|
|
|
+ assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());
|
|
|
+ assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
|
|
|
+ assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();
|
|
|
+ assertThat("MD5").isEqualTo(determineAlgorithmNameFromBase64EncodedToken(cookie.getValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void loginSuccessWhenCustomEncodingAlgorithmThenContainsAlgorithmName() {
|
|
|
+ MockHttpServletRequest request = new MockHttpServletRequest();
|
|
|
+ request.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, "true");
|
|
|
+ MockHttpServletResponse response = new MockHttpServletResponse();
|
|
|
+ this.services = new TokenBasedRememberMeServices("key", this.uds, RememberMeTokenAlgorithm.SHA256);
|
|
|
+ this.services.loginSuccess(request, response,
|
|
|
+ new TestingAuthenticationToken("someone", "password", "ROLE_ABC"));
|
|
|
+ Cookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
|
|
+ assertThat(cookie).isNotNull();
|
|
|
+ assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());
|
|
|
+ assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
|
|
|
+ assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();
|
|
|
+ assertThat("SHA256").isEqualTo(determineAlgorithmNameFromBase64EncodedToken(cookie.getValue()));
|
|
|
+ }
|
|
|
+
|
|
|
// SEC-933
|
|
|
@Test
|
|
|
public void obtainPasswordReturnsNullForTokenWithNullCredentials() {
|
|
@@ -318,4 +452,19 @@ public class TokenBasedRememberMeServicesTests {
|
|
|
assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void constructorWhenEncodingAlgorithmNullThenException() {
|
|
|
+ assertThatExceptionOfType(IllegalArgumentException.class)
|
|
|
+ .isThrownBy(() -> new TokenBasedRememberMeServices("key", this.uds, null))
|
|
|
+ .withMessage("encodingAlgorithm cannot be null");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void constructorWhenNoEncodingAlgorithmSpecifiedThenMd5() {
|
|
|
+ TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("key", this.uds);
|
|
|
+ RememberMeTokenAlgorithm encodingAlgorithm = (RememberMeTokenAlgorithm) ReflectionTestUtils
|
|
|
+ .getField(rememberMeServices, "encodingAlgorithm");
|
|
|
+ assertThat(encodingAlgorithm).isSameAs(RememberMeTokenAlgorithm.MD5);
|
|
|
+ }
|
|
|
+
|
|
|
}
|