SEC-588: PersistentTokenBasedRememberMeServices implementation.
This commit is contained in:
+277
@@ -0,0 +1,277 @@
|
||||
package org.springframework.security.ui.rememberme;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.GrantedAuthorityImpl;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.userdetails.User;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UserDetailsService;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AbstractRememberMeServicesTests {
|
||||
User joe = new User("joe", "password", true, true,true,true, new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
|
||||
|
||||
@Test(expected = InvalidCookieException.class)
|
||||
public void nonBase64CookieShouldBeDetected() {
|
||||
new MockRememberMeServices().decodeCookie("nonBase64CookieValue%");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cookieShouldBeCorrectlyEncodedAndDecoded() {
|
||||
String[] cookie = new String[] {"the", "cookie", "tokens", "blah"};
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
|
||||
String encoded = services.encodeCookie(cookie);
|
||||
// '=' aren't alowed in version 0 cookies.
|
||||
assertFalse(encoded.endsWith("="));
|
||||
String[] decoded = services.decodeCookie(encoded);
|
||||
|
||||
assertEquals(4, decoded.length);
|
||||
assertEquals("the", decoded[0]);
|
||||
assertEquals("cookie", decoded[1]);
|
||||
assertEquals("tokens", decoded[2]);
|
||||
assertEquals("blah", decoded[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoLoginShouldReturnNullIfNoLoginCookieIsPresented() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
assertNull(services.autoLogin(request, response));
|
||||
|
||||
// shouldn't try to invalidate our cookie
|
||||
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY));
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
// set non-login cookie
|
||||
request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")});
|
||||
assertNull(services.autoLogin(request, response));
|
||||
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulAutoLoginReturnsExpectedAuthentication() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joe, false));
|
||||
assertNotNull(services.getUserDetailsService());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Authentication result = services.autoLogin(request, response);
|
||||
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoLoginShouldFailIfInvalidCookieExceptionIsRaised() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
// Wrong number of tokes
|
||||
request.setCookies(createLoginCookie("cookie:1"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Authentication result = services.autoLogin(request, response);
|
||||
|
||||
assertNull(result);
|
||||
|
||||
assertCookieCancelled(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoLoginShouldFailIfUserNotFound() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Authentication result = services.autoLogin(request, response);
|
||||
|
||||
assertNull(result);
|
||||
|
||||
assertCookieCancelled(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoLoginShouldFailIfUserAccountIsLocked() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
User joeLocked = new User("joe", "password",false,true,true,true,joe.getAuthorities());
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joeLocked, false));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Authentication result = services.autoLogin(request, response);
|
||||
|
||||
assertNull(result);
|
||||
|
||||
assertCookieCancelled(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginFailShouldCancelCookie() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setContextPath("contextpath");
|
||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
services.loginFail(request, response);
|
||||
|
||||
assertCookieCancelled(response);
|
||||
}
|
||||
|
||||
@Test(expected = CookieTheftException.class)
|
||||
public void cookieTheftExceptionShouldBeRethrown() {
|
||||
MockRememberMeServices services = new MockRememberMeServices() {
|
||||
protected String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
|
||||
throw new CookieTheftException("Pretending cookie was stolen");
|
||||
}
|
||||
};
|
||||
|
||||
services.setUserDetailsService(new MockAuthenticationDao(joe, false));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
services.autoLogin(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccessCallsOnLoginSuccessCorrectly() {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Authentication auth = new UsernamePasswordAuthenticationToken("joe","password");
|
||||
|
||||
// No parameter set
|
||||
services = new MockRememberMeServices();
|
||||
services.loginSuccess(request, response, auth);
|
||||
assertFalse(services.loginSuccessCalled);
|
||||
|
||||
// Parameter set to true
|
||||
services = new MockRememberMeServices();
|
||||
request.setParameter(MockRememberMeServices.DEFAULT_PARAMETER, "true");
|
||||
services.loginSuccess(request, response, auth);
|
||||
assertTrue(services.loginSuccessCalled);
|
||||
|
||||
// Different parameter name, set to true
|
||||
services = new MockRememberMeServices();
|
||||
services.setParameter("my_parameter");
|
||||
request.setParameter("my_parameter", "true");
|
||||
services.loginSuccess(request, response, auth);
|
||||
assertTrue(services.loginSuccessCalled);
|
||||
|
||||
|
||||
// Parameter set to false
|
||||
services = new MockRememberMeServices();
|
||||
request.setParameter(MockRememberMeServices.DEFAULT_PARAMETER, "false");
|
||||
services.loginSuccess(request, response, auth);
|
||||
assertFalse(services.loginSuccessCalled);
|
||||
|
||||
// alwaysRemember set to true
|
||||
services = new MockRememberMeServices();
|
||||
services.setAlwaysRemember(true);
|
||||
services.loginSuccess(request, response, auth);
|
||||
assertTrue(services.loginSuccessCalled);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void makeValidCookieUsesCorrectNamePathAndValue() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setContextPath("contextpath");
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
services.setCookieName("mycookiename");
|
||||
Cookie cookie = services.makeValidCookie("mycookie", request, 1000);
|
||||
|
||||
assertTrue(cookie.getValue().equals("mycookie"));
|
||||
assertTrue(cookie.getName().equals("mycookiename"));
|
||||
assertTrue(cookie.getPath().equals("contextpath"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Cookie[] createLoginCookie(String cookieToken) {
|
||||
MockRememberMeServices services = new MockRememberMeServices();
|
||||
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY,
|
||||
services.encodeCookie(StringUtils.delimitedListToStringArray(cookieToken, ":")));
|
||||
|
||||
return new Cookie[] {cookie};
|
||||
}
|
||||
|
||||
private void assertCookieCancelled(MockHttpServletResponse response) {
|
||||
Cookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY);
|
||||
assertNotNull(returnedCookie);
|
||||
assertEquals(0, returnedCookie.getMaxAge());
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
private class MockRememberMeServices extends AbstractRememberMeServices {
|
||||
boolean loginSuccessCalled;
|
||||
|
||||
private MockRememberMeServices() {
|
||||
setKey("key");
|
||||
}
|
||||
|
||||
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
|
||||
loginSuccessCalled = true;
|
||||
}
|
||||
|
||||
protected String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) throws RememberMeAuthenticationException {
|
||||
if(cookieTokens.length != 3) {
|
||||
throw new InvalidCookieException("deliberate exception");
|
||||
}
|
||||
|
||||
return "joe";
|
||||
}
|
||||
}
|
||||
|
||||
private class MockAuthenticationDao implements UserDetailsService {
|
||||
private UserDetails toReturn;
|
||||
private boolean throwException;
|
||||
|
||||
public MockAuthenticationDao(UserDetails toReturn, boolean throwException) {
|
||||
this.toReturn = toReturn;
|
||||
this.throwException = throwException;
|
||||
}
|
||||
|
||||
public UserDetails loadUserByUsername(String username) {
|
||||
if (throwException) {
|
||||
throw new UsernameNotFoundException("as requested by mock");
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package org.springframework.security.ui.rememberme;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PersistentTokenBasedRememberMeServicesTests {
|
||||
private PersistentTokenBasedRememberMeServices services;
|
||||
|
||||
@Before
|
||||
public void setUpData() throws Exception {
|
||||
services = new PersistentTokenBasedRememberMeServices();
|
||||
}
|
||||
|
||||
@Test(expected = InvalidCookieException.class)
|
||||
public void loginIsRejectedWithWrongNumberOfCookieTokens() {
|
||||
services.setCookieName("mycookiename");
|
||||
services.processAutoLoginCookie(new String[] {"series", "token", "extra"}, new MockHttpServletRequest(),
|
||||
new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test(expected = RememberMeAuthenticationException.class)
|
||||
public void loginIsRejectedWhenNoTokenMatchingSeriesIsFound() {
|
||||
services.setCookieName("mycookiename");
|
||||
services.setTokenRepository(new MockTokenRepository(null));
|
||||
services.processAutoLoginCookie(new String[] {"series", "token"}, new MockHttpServletRequest(),
|
||||
new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test(expected = CookieTheftException.class)
|
||||
public void cookieTheftIsDetectedWhenSeriesAndTokenDontMatch() {
|
||||
services.setCookieName("mycookiename");
|
||||
PersistentRememberMeToken token = new PersistentRememberMeToken("joe", "series","wrongtoken", new Date());
|
||||
services.setTokenRepository(new MockTokenRepository(token));
|
||||
services.processAutoLoginCookie(new String[] {"series", "token"}, new MockHttpServletRequest(),
|
||||
new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulAutoLoginCreatesNewTokenAndCookieWithSameSeries() {
|
||||
services.setCookieName("mycookiename");
|
||||
MockTokenRepository repo =
|
||||
new MockTokenRepository(new PersistentRememberMeToken("joe", "series","token", new Date()));
|
||||
services.setTokenRepository(repo);
|
||||
// 12 => b64 length will be 16
|
||||
services.setTokenLength(12);
|
||||
services.processAutoLoginCookie(new String[] {"series", "token"}, new MockHttpServletRequest(),
|
||||
new MockHttpServletResponse());
|
||||
assertEquals("series",repo.getStoredToken().getSeries());
|
||||
assertEquals(16, repo.getStoredToken().getTokenValue().length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccessCreatesNewTokenAndCookieWithNewSeries() {
|
||||
services.setAlwaysRemember(true);
|
||||
MockTokenRepository repo = new MockTokenRepository(null);
|
||||
services.setTokenRepository(repo);
|
||||
services.setTokenLength(12);
|
||||
services.setSeriesLength(12);
|
||||
services.loginSuccess(new MockHttpServletRequest(),
|
||||
new MockHttpServletResponse(), new UsernamePasswordAuthenticationToken("joe","password"));
|
||||
assertEquals(16, repo.getStoredToken().getSeries().length());
|
||||
assertEquals(16, repo.getStoredToken().getTokenValue().length());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class MockTokenRepository implements PersistentTokenRepository {
|
||||
private PersistentRememberMeToken storedToken;
|
||||
|
||||
private MockTokenRepository(PersistentRememberMeToken token) {
|
||||
storedToken = token;
|
||||
}
|
||||
|
||||
public void saveToken(PersistentRememberMeToken token) {
|
||||
storedToken = token;
|
||||
}
|
||||
|
||||
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
|
||||
return storedToken;
|
||||
}
|
||||
|
||||
public void removeAllTokens(String username) {
|
||||
}
|
||||
|
||||
PersistentRememberMeToken getStoredToken() {
|
||||
return storedToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user