1 package com.atlassian.seraph.service.rememberme;
2
3 import com.atlassian.seraph.spi.rememberme.RememberMeConfiguration;
4 import com.atlassian.seraph.spi.rememberme.RememberMeTokenDao;
5 import junit.framework.TestCase;
6 import org.mockito.ArgumentCaptor;
7 import static org.mockito.Matchers.eq;
8 import static org.mockito.Mockito.mock;
9 import static org.mockito.Mockito.times;
10 import static org.mockito.Mockito.verify;
11 import static org.mockito.Mockito.when;
12
13 import javax.servlet.http.Cookie;
14 import javax.servlet.http.HttpServletRequest;
15 import javax.servlet.http.HttpServletResponse;
16
17 import java.util.concurrent.TimeUnit;
18
19
20
21 public class TestDefaultRememberMeService extends TestCase
22 {
23 private HttpServletRequest request;
24 private HttpServletResponse response;
25 private RememberMeConfiguration configuration;
26 private RememberMeTokenGenerator tokenGenerator;
27 private RememberMeTokenDao tokenDao;
28 private DefaultRememberMeService service;
29 private RememberMeToken fredsToken;
30
31 private static final String FRED_FLINTSTONE = "fred flintstone";
32 private static final String RANDOM_STRING = "randomString";
33 private static final String COOKIE_NAME = "cookieName";
34 private static final String COOKIE_DOMAIN = "cookiedomain";
35 private static final String COOKIE_PATH = "cookiepath";
36 private static final int COOKIE_AGE = 1673;
37 private static final long TOKEN_ID = 456L;
38 private static final String COLON = "%3A";
39 private static final String COOKIE_VALUE = "456" + COLON + "randomString";
40 private static final String BAD_COOKIE_VALUE = "456" + COLON + "badValue";
41
42 @Override
43 protected void setUp() throws Exception
44 {
45 request = mock(HttpServletRequest.class);
46 response = mock(HttpServletResponse.class);
47 configuration = mock(RememberMeConfiguration.class);
48 tokenGenerator = mock(RememberMeTokenGenerator.class);
49 tokenDao = mock(RememberMeTokenDao.class);
50
51
52 service = new DefaultRememberMeService(configuration, tokenDao, tokenGenerator);
53
54 fredsToken = DefaultRememberMeToken.builder(TOKEN_ID, RANDOM_STRING).setUserName(FRED_FLINTSTONE)
55 .setCreatedTime(System.currentTimeMillis()).build();
56
57 when(tokenGenerator.generateToken(FRED_FLINTSTONE)).thenReturn(fredsToken);
58 when(configuration.getCookieName()).thenReturn(COOKIE_NAME);
59 when(configuration.getCookieDomain(request)).thenReturn(COOKIE_DOMAIN);
60 when(configuration.getCookiePath(request)).thenReturn(COOKIE_PATH);
61 when(configuration.getCookieMaxAgeInSeconds()).thenReturn(COOKIE_AGE);
62 when(configuration.isCookieHttpOnly()).thenReturn(false);
63 }
64
65 @Override
66 protected void tearDown() throws Exception
67 {
68 }
69
70 public void testAddRememberMe_NoCookies()
71 {
72 when(request.getCookies()).thenReturn(null);
73 when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
74
75 service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
76
77 assertCookieValuesAreSet(response, false);
78 }
79
80 public void testAddRememberMe_ZeroCookies()
81 {
82 when(request.getCookies()).thenReturn(new Cookie[0]);
83 when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
84
85 service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
86
87 assertCookieValuesAreSet(response, false);
88 }
89
90 public void testAddRememberMe_NoCookies_HttpOnly()
91 {
92 when(configuration.isCookieHttpOnly()).thenReturn(true);
93 when(request.getCookies()).thenReturn(new Cookie[0]);
94 when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
95
96 service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
97
98 assertCookieValuesAreSet(response, true);
99 }
100
101 public void testAddRememberMe_WithExistingCookies()
102 {
103 Cookie existingCookie = new Cookie(COOKIE_NAME, "oldValue");
104 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
105 when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
106
107 service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
108
109 assertCookieValuesAreSet(response, false);
110 }
111
112 public void testAddRememberMe_WithExistingCookies_HttpOnly()
113 {
114 when(configuration.isCookieHttpOnly()).thenReturn(true);
115
116 Cookie existingCookie = new Cookie(COOKIE_NAME, "oldValue");
117 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
118 when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
119
120 service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
121
122 assertCookieValuesAreSet(response, true);
123 }
124
125 public void testRemoveRememberMeCookie_NoCookies()
126 {
127 when(request.getCookies()).thenReturn(new Cookie[] { });
128
129 service.removeRememberMeCookie(request, response);
130
131 verify(response, times(0)).addHeader(null, null);
132 }
133
134 public void testRemoveRememberMeCookie_WithExistingCookie()
135 {
136 Cookie existingCookie = new Cookie(COOKIE_NAME, "123:ABCD");
137
138 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
139
140 service.removeRememberMeCookie(request, response);
141
142 assertThatExistingCookieIsRemoved(false);
143 verify(tokenDao).remove(123L);
144 }
145
146 public void testRemoveRememberMeCookie_WithExistingCookie_HttpOnly()
147 {
148 Cookie existingCookie = new Cookie(COOKIE_NAME, "123:ABCD");
149
150 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
151 when(configuration.isCookieHttpOnly()).thenReturn(true);
152
153 service.removeRememberMeCookie(request, response);
154
155 assertThatExistingCookieIsRemoved(true);
156 verify(tokenDao).remove(123L);
157 }
158
159 private void assertCookieValuesAreSet(final HttpServletResponse response, boolean httpOnlyUsed)
160 {
161 if (httpOnlyUsed)
162 {
163 ArgumentCaptor<String> cookieCaptor = ArgumentCaptor.forClass(String.class);
164 verify(response, times(1)).addHeader(eq("Set-Cookie"), cookieCaptor.capture());
165
166 final String header = cookieCaptor.getValue();
167 assertTrue(header.indexOf(COOKIE_NAME + "=" + COOKIE_VALUE) == 0);
168 assertTrue(header.indexOf("; Domain=" + COOKIE_DOMAIN) != -1);
169 assertTrue(header.indexOf("; Path=" + COOKIE_PATH) != -1);
170 assertTrue(header.indexOf("; HttpOnly") != -1);
171 }
172 else
173 {
174 ArgumentCaptor<Cookie> cookieCaptor = ArgumentCaptor.forClass(Cookie.class);
175 verify(response, times(1)).addCookie(cookieCaptor.capture());
176
177 final Cookie cooky = cookieCaptor.getValue();
178 assertEquals(COOKIE_NAME, cooky.getName());
179 assertEquals(COOKIE_DOMAIN, cooky.getDomain());
180 assertEquals(COOKIE_PATH, cooky.getPath());
181 assertEquals(COOKIE_AGE, cooky.getMaxAge());
182 assertEquals(COOKIE_VALUE, cooky.getValue());
183 }
184 }
185
186 private void assertThatExistingCookieIsRemoved(boolean isHttpOnly)
187 {
188 if (isHttpOnly)
189 {
190 ArgumentCaptor<String> cookieCaptor = ArgumentCaptor.forClass(String.class);
191 verify(response, times(1)).addHeader(eq("Set-Cookie"), cookieCaptor.capture());
192
193 final String header = cookieCaptor.getValue();
194
195 assertTrue(header.indexOf(COOKIE_NAME + "=" + "") == 0);
196
197 assertTrue(header.indexOf("; Domain=" + COOKIE_DOMAIN) != -1);
198 assertTrue(header.indexOf("; Path=" + COOKIE_PATH) != -1);
199 assertTrue(header.indexOf("; HttpOnly") != -1);
200
201 }
202 else
203 {
204 ArgumentCaptor<Cookie> cookieCaptor = ArgumentCaptor.forClass(Cookie.class);
205 verify(response, times(1)).addCookie(cookieCaptor.capture());
206
207 final Cookie cooky = cookieCaptor.getValue();
208 assertEquals(COOKIE_NAME, cooky.getName());
209 assertEquals(COOKIE_DOMAIN, cooky.getDomain());
210 assertEquals(COOKIE_PATH, cooky.getPath());
211 assertEquals(0, cooky.getMaxAge());
212 assertEquals("", cooky.getValue());
213 }
214
215 }
216
217 public void testGetRememberMeCookieAuthenticatedUsername_NoCookies()
218 {
219 when(request.getCookies()).thenReturn(new Cookie[0]);
220
221 final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
222 assertNull(username);
223 }
224
225 public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_ButItDoesNotMatch()
226 {
227 Cookie existingCookie = new Cookie(COOKIE_NAME, BAD_COOKIE_VALUE);
228 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
229
230 when(tokenDao.findById(TOKEN_ID)).thenReturn(fredsToken);
231
232 final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
233 assertNull(username);
234
235 assertThatExistingCookieIsRemoved(false);
236 }
237
238
239 public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_ButTheAppDoesntLikeIt()
240 {
241 Cookie existingCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
242 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
243
244 when(tokenDao.findById(TOKEN_ID)).thenReturn(null);
245
246 final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
247 assertNull(username);
248
249 assertThatExistingCookieIsRemoved(false);
250 }
251
252 public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_AndItsGood()
253 {
254 Cookie existingCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
255 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
256
257 when(tokenDao.findById(TOKEN_ID)).thenReturn(fredsToken);
258
259 final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
260 assertEquals(FRED_FLINTSTONE, username);
261 }
262
263 public void testGetRememberMeCookieAuthenticatedUsername_expiredToken()
264 {
265 Cookie existingCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
266 when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
267
268 RememberMeToken token = DefaultRememberMeToken.builder(TOKEN_ID, RANDOM_STRING).setUserName(FRED_FLINTSTONE)
269 .setCreatedTime(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(COOKIE_AGE) - 1).build();
270
271 when(tokenDao.findById(TOKEN_ID)).thenReturn(token);
272
273 final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
274 assertNull(username);
275 assertThatExistingCookieIsRemoved(false);
276
277 }
278 }