/* * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * 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 static org.mockito.Mockito.*; import static org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.*; import java.util.*; import javax.servlet.http.Cookie; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; 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.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.StringUtils; /** * Tests * {@link org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices} * . * * @author Ben Alex */ public class TokenBasedRememberMeServicesTests { private UserDetailsService uds; private UserDetails user = new User("someone", "password", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_ABC")); private TokenBasedRememberMeServices services; // ~ Methods // ======================================================================================================== @Before public void createTokenBasedRememberMeServices() { uds = mock(UserDetailsService.class); services = new TokenBasedRememberMeServices("key", uds); } void udsWillReturnUser() { when(uds.loadUserByUsername(any(String.class))).thenReturn(user); } void udsWillThrowNotFound() { when(uds.loadUserByUsername(any(String.class))).thenThrow( new UsernameNotFoundException("")); } private long determineExpiryTimeFromBased64EncodedToken(String validToken) { String cookieAsPlainText = new String(Base64.decodeBase64(validToken.getBytes())); String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":"); if (cookieTokens.length == 3) { try { return Long.parseLong(cookieTokens[1]); } catch (NumberFormatException ignored) { } } return -1; } private String generateCorrectCookieContentForToken(long expiryTime, String username, String password, String key) { // format is: // username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + // password + ":" + key) String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key); String tokenValue = username + ":" + expiryTime + ":" + signatureValue; return new String(Base64.encodeBase64(tokenValue.getBytes())); } @Test public void autoLoginReturnsNullIfNoCookiePresented() throws Exception { MockHttpServletResponse response = new MockHttpServletResponse(); Authentication result = services .autoLogin(new MockHttpServletRequest(), response); assertThat(result).isNull(); // No cookie set assertThat(response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull(); } @Test public void autoLoginIgnoresUnrelatedCookie() throws Exception { Cookie cookie = new Cookie("unrelated_cookie", "foobar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); Authentication result = services.autoLogin(request, response); assertThat(result).isNull(); assertThat(response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull(); } @Test public void autoLoginReturnsNullForExpiredCookieAndClearsCookie() throws Exception { Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, generateCorrectCookieContentForToken( System.currentTimeMillis() - 1000000, "someone", "password", "key")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginReturnsNullAndClearsCookieIfMissingThreeTokensInCookieValue() throws Exception { Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, new String( Base64.encodeBase64("x".getBytes()))); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginClearsNonBase64EncodedCookie() throws Exception { Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "NOT_BASE_64_ENCODED"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginClearsCookieIfSignatureBlocksDoesNotMatchExpectedValue() throws Exception { udsWillReturnUser(); Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, generateCorrectCookieContentForToken( System.currentTimeMillis() + 1000000, "someone", "password", "WRONG_KEY")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginClearsCookieIfTokenDoesNotContainANumberInCookieValue() throws Exception { Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, new String( Base64.encodeBase64("username:NOT_A_NUMBER:signature".getBytes()))); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginClearsCookieIfUserNotFound() throws Exception { udsWillThrowNotFound(); Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, generateCorrectCookieContentForToken( System.currentTimeMillis() + 1000000, "someone", "password", "key")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); assertThat(services.autoLogin(request, response)).isNull(); Cookie returnedCookie = response .getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(returnedCookie).isNotNull(); assertThat(returnedCookie.getMaxAge()).isEqualTo(0); } @Test public void autoLoginWithValidTokenAndUserSucceeds() throws Exception { udsWillReturnUser(); Cookie cookie = new Cookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, generateCorrectCookieContentForToken( System.currentTimeMillis() + 1000000, "someone", "password", "key")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(cookie); MockHttpServletResponse response = new MockHttpServletResponse(); Authentication result = services.autoLogin(request, response); assertThat(result).isNotNull(); assertThat(result.getPrincipal()).isEqualTo(user); } @Test public void testGettersSetters() { assertThat(services.getUserDetailsService()).isEqualTo(uds); assertThat(services.getKey()).isEqualTo("key"); assertThat(services.getParameter()).isEqualTo(DEFAULT_PARAMETER); services.setParameter("some_param"); assertThat(services.getParameter()).isEqualTo("some_param"); services.setTokenValiditySeconds(12); assertThat(services.getTokenValiditySeconds()).isEqualTo(12); } @Test public void loginFailClearsCookie() { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); services.loginFail(request, response); Cookie cookie = response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(cookie).isNotNull(); assertThat(cookie.getMaxAge()).isEqualTo(0); } @Test public void loginSuccessIgnoredIfParameterNotSetOrFalse() { TokenBasedRememberMeServices services = new TokenBasedRememberMeServices("key", new AbstractRememberMeServicesTests.MockUserDetailsService(null, false)); MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(DEFAULT_PARAMETER, "false"); MockHttpServletResponse response = new MockHttpServletResponse(); services.loginSuccess(request, response, new TestingAuthenticationToken( "someone", "password", "ROLE_ABC")); Cookie cookie = response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(cookie).isNull(); } @Test public void loginSuccessNormalWithNonUserDetailsBasedPrincipalSetsExpectedCookie() { // SEC-822 services.setTokenValiditySeconds(500000000); MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(TokenBasedRememberMeServices.DEFAULT_PARAMETER, "true"); MockHttpServletResponse response = new MockHttpServletResponse(); services.loginSuccess(request, response, new TestingAuthenticationToken( "someone", "password", "ROLE_ABC")); Cookie cookie = response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); String expiryTime = services.decodeCookie(cookie.getValue())[1]; long expectedExpiryTime = 1000L * 500000000; expectedExpiryTime += System.currentTimeMillis(); assertThat(Long.parseLong(expiryTime) > expectedExpiryTime - 10000).isTrue(); assertThat(cookie).isNotNull(); assertThat(cookie.getMaxAge()).isEqualTo(services.getTokenValiditySeconds()); assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); assertThat(new Date().before(new Date( determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue(); } @Test public void loginSuccessNormalWithUserDetailsBasedPrincipalSetsExpectedCookie() { MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(TokenBasedRememberMeServices.DEFAULT_PARAMETER, "true"); MockHttpServletResponse response = new MockHttpServletResponse(); services.loginSuccess(request, response, new TestingAuthenticationToken( "someone", "password", "ROLE_ABC")); Cookie cookie = response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(cookie).isNotNull(); assertThat(cookie.getMaxAge()).isEqualTo(services.getTokenValiditySeconds()); assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); assertThat(new Date().before(new Date( determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue(); } // SEC-933 @Test public void obtainPasswordReturnsNullForTokenWithNullCredentials() throws Exception { TestingAuthenticationToken token = new TestingAuthenticationToken("username", null); assertThat(services.retrievePassword(token)).isNull(); } // SEC-949 @Test public void negativeValidityPeriodIsSetOnCookieButExpiryTimeRemainsAtTwoWeeks() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(DEFAULT_PARAMETER, "true"); MockHttpServletResponse response = new MockHttpServletResponse(); services.setTokenValiditySeconds(-1); services.loginSuccess(request, response, new TestingAuthenticationToken( "someone", "password", "ROLE_ABC")); Cookie cookie = response.getCookie(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); assertThat(cookie).isNotNull(); // Check the expiry time is within 50ms of two weeks from current time assertThat(determineExpiryTimeFromBased64EncodedToken(cookie.getValue()) - System.currentTimeMillis() > TWO_WEEKS_S - 50).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(-1); assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); } }