/*
* Copyright 2002-2016 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
*
* http://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.web.authentication.rememberme;
import static org.assertj.core.api.Assertions.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.rememberme.CookieTheftException;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
/**
* @author Luke Taylor
*/
public class PersistentTokenBasedRememberMeServicesTests {
private PersistentTokenBasedRememberMeServices services;
private MockTokenRepository repo;
@Before
public void setUpData() throws Exception {
services = new PersistentTokenBasedRememberMeServices("key",
new AbstractRememberMeServicesTests.MockUserDetailsService(
AbstractRememberMeServicesTests.joe, false),
new InMemoryTokenRepositoryImpl());
services.setCookieName("mycookiename");
// Default to 100 days (see SEC-1081).
services.setTokenValiditySeconds(100 * 24 * 60 * 60);
services.afterPropertiesSet();
}
@Test(expected = InvalidCookieException.class)
public void loginIsRejectedWithWrongNumberOfCookieTokens() {
services.processAutoLoginCookie(new String[] { "series", "token", "extra" },
new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test(expected = RememberMeAuthenticationException.class)
public void loginIsRejectedWhenNoTokenMatchingSeriesIsFound() {
services = create(null);
services.processAutoLoginCookie(new String[] { "series", "token" },
new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test(expected = RememberMeAuthenticationException.class)
public void loginIsRejectedWhenTokenIsExpired() {
services = create(new PersistentRememberMeToken("joe", "series", "token",
new Date(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(1) - 100)));
services.setTokenValiditySeconds(1);
services.processAutoLoginCookie(new String[] { "series", "token" },
new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test(expected = CookieTheftException.class)
public void cookieTheftIsDetectedWhenSeriesAndTokenDontMatch() {
services = create(new PersistentRememberMeToken("joe", "series", "wrongtoken",
new Date()));
services.processAutoLoginCookie(new String[] { "series", "token" },
new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test
public void successfulAutoLoginCreatesNewTokenAndCookieWithSameSeries() {
services = create(new PersistentRememberMeToken("joe", "series", "token",
new Date()));
// 12 => b64 length will be 16
services.setTokenLength(12);
MockHttpServletResponse response = new MockHttpServletResponse();
services.processAutoLoginCookie(new String[] { "series", "token" },
new MockHttpServletRequest(), response);
assertThat(repo.getStoredToken().getSeries()).isEqualTo("series");
assertThat(repo.getStoredToken().getTokenValue().length()).isEqualTo(16);
String[] cookie = services.decodeCookie(response.getCookie("mycookiename")
.getValue());
assertThat(cookie[0]).isEqualTo("series");
assertThat(cookie[1]).isEqualTo(repo.getStoredToken().getTokenValue());
}
@Test
public void loginSuccessCreatesNewTokenAndCookieWithNewSeries() {
services = create(null);
services.setAlwaysRemember(true);
services.setTokenLength(12);
services.setSeriesLength(12);
MockHttpServletResponse response = new MockHttpServletResponse();
services.loginSuccess(new MockHttpServletRequest(), response,
new UsernamePasswordAuthenticationToken("joe", "password"));
assertThat(repo.getStoredToken().getSeries().length()).isEqualTo(16);
assertThat(repo.getStoredToken().getTokenValue().length()).isEqualTo(16);
String[] cookie = services.decodeCookie(response.getCookie("mycookiename")
.getValue());
assertThat(cookie[0]).isEqualTo(repo.getStoredToken().getSeries());
assertThat(cookie[1]).isEqualTo(repo.getStoredToken().getTokenValue());
}
@Test
public void logoutClearsUsersTokenAndCookie() throws Exception {
Cookie cookie = new Cookie("mycookiename", "somevalue");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setCookies(cookie);
MockHttpServletResponse response = new MockHttpServletResponse();
services = create(new PersistentRememberMeToken("joe", "series", "token",
new Date()));
services.logout(request, response, new TestingAuthenticationToken("joe",
"somepass", "SOME_AUTH"));
Cookie returnedCookie = response.getCookie("mycookiename");
assertThat(returnedCookie).isNotNull();
assertThat(returnedCookie.getMaxAge()).isEqualTo(0);
// SEC-1280
services.logout(request, response, null);
}
private PersistentTokenBasedRememberMeServices create(PersistentRememberMeToken token) {
repo = new MockTokenRepository(token);
PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices(
"key", new AbstractRememberMeServicesTests.MockUserDetailsService(
AbstractRememberMeServicesTests.joe, false), repo);
services.setCookieName("mycookiename");
return services;
}
private class MockTokenRepository implements PersistentTokenRepository {
private PersistentRememberMeToken storedToken;
private MockTokenRepository(PersistentRememberMeToken token) {
storedToken = token;
}
public void createNewToken(PersistentRememberMeToken token) {
storedToken = token;
}
public void updateToken(String series, String tokenValue, Date lastUsed) {
storedToken = new PersistentRememberMeToken(storedToken.getUsername(),
storedToken.getSeries(), tokenValue, lastUsed);
}
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
return storedToken;
}
public void removeUserTokens(String username) {
}
PersistentRememberMeToken getStoredToken() {
return storedToken;
}
}
}