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  /**
18   */
19  public class TestDefaultRememberMeService extends TestCase
20  {
21      private HttpServletRequest request;
22      private HttpServletResponse response;
23      private RememberMeConfiguration configuration;
24      private RememberMeTokenGenerator tokenGenerator;
25      private RememberMeTokenDao tokenDao;
26      private DefaultRememberMeService service;
27      private RememberMeToken fredsToken;
28  
29      private static final String FRED_FLINTSTONE = "fred flintstone";
30      private static final String RANDOM_STRING = "randomString";
31      private static final String COOKIE_NAME = "cookieName";
32      private static final String COOKIE_DOMAIN = "cookiedomain";
33      private static final String COOKIE_PATH = "cookiepath";
34      private static final int COOKIE_AGE = 1673;
35      private static final long TOKEN_ID = 456L;
36      private static final String COLON = "%3A";
37      private static final String COOKIE_VALUE = "456" + COLON + "randomString";
38      private static final String BAD_COOKIE_VALUE = "456" + COLON + "badValue";
39  
40      @Override
41      protected void setUp() throws Exception
42      {
43          request = mock(HttpServletRequest.class);
44          response = mock(HttpServletResponse.class);
45          configuration = mock(RememberMeConfiguration.class);
46          tokenGenerator = mock(RememberMeTokenGenerator.class);
47          tokenDao = mock(RememberMeTokenDao.class);
48  
49  
50          service = new DefaultRememberMeService(configuration, tokenDao, tokenGenerator);
51  
52          fredsToken = DefaultRememberMeToken.builder(TOKEN_ID, RANDOM_STRING).setUserName(FRED_FLINTSTONE).build();
53  
54          when(tokenGenerator.generateToken(FRED_FLINTSTONE)).thenReturn(fredsToken);
55          when(configuration.getCookieName()).thenReturn(COOKIE_NAME);
56          when(configuration.getCookieDomain(request)).thenReturn(COOKIE_DOMAIN);
57          when(configuration.getCookiePath(request)).thenReturn(COOKIE_PATH);
58          when(configuration.getCookieMaxAgeInSeconds()).thenReturn(COOKIE_AGE);
59          when(configuration.isCookieHttpOnly()).thenReturn(false);
60      }
61  
62      @Override
63      protected void tearDown() throws Exception
64      {
65      }
66  
67      public void testAddRememberMe_NoCookies()
68      {
69          when(request.getCookies()).thenReturn(new Cookie[0]);
70          when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
71  
72          service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
73  
74          assertCookieValuesAreSet(response, false);
75      }
76  
77      public void testAddRememberMe_NoCookies_HttpOnly()
78      {
79          when(configuration.isCookieHttpOnly()).thenReturn(true);
80          when(request.getCookies()).thenReturn(new Cookie[0]);
81          when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
82  
83          service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
84  
85          assertCookieValuesAreSet(response, true);
86      }
87  
88      public void testAddRememberMe_WithExistingCookies()
89      {
90          Cookie existingCookie = new Cookie(COOKIE_NAME, "oldValue");
91          when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
92          when(tokenDao.save(fredsToken)).thenReturn(fredsToken);
93  
94          service.addRememberMeCookie(request, response, FRED_FLINTSTONE);
95  
96          assertCookieValuesAreSet(response, false);
97      }
98  
99      public void testAddRememberMe_WithExistingCookies_HttpOnly()
100     {
101         when(configuration.isCookieHttpOnly()).thenReturn(true);
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, true);
110     }
111 
112     public void testRemoveRememberMeCookie_NoCookies()
113     {
114         when(request.getCookies()).thenReturn(new Cookie[] { });
115 
116         service.removeRememberMeCookie(request, response);
117 
118         verify(response, times(0)).addHeader(null, null);
119     }
120 
121     public void testRemoveRememberMeCookie_WithExistingCookie()
122     {
123         Cookie existingCookie = new Cookie(COOKIE_NAME, "oldValue");
124 
125         when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
126 
127         service.removeRememberMeCookie(request, response);
128 
129         assertThatExistingCookieIsRemoved(false);
130     }
131 
132      public void testRemoveRememberMeCookie_WithExistingCookie_HttpOnly()
133     {
134         Cookie existingCookie = new Cookie(COOKIE_NAME, "oldValue");
135 
136         when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
137         when(configuration.isCookieHttpOnly()).thenReturn(true);
138 
139         service.removeRememberMeCookie(request, response);
140 
141         assertThatExistingCookieIsRemoved(true);
142     }
143 
144     private void assertCookieValuesAreSet(final HttpServletResponse response, boolean httpOnlyUsed)
145     {
146         if (httpOnlyUsed)
147         {
148             ArgumentCaptor<String> cookieCaptor = ArgumentCaptor.forClass(String.class);
149             verify(response, times(1)).addHeader(eq("Set-Cookie"), cookieCaptor.capture());
150 
151             final String header = cookieCaptor.getValue();
152             assertTrue(header.indexOf(COOKIE_NAME + "=" + COOKIE_VALUE) == 0);
153             assertTrue(header.indexOf("; Domain=" + COOKIE_DOMAIN) != -1);
154             assertTrue(header.indexOf("; Path=" + COOKIE_PATH) != -1);
155             assertTrue(header.indexOf("; HttpOnly") != -1);
156         }
157         else
158         {
159             ArgumentCaptor<Cookie> cookieCaptor = ArgumentCaptor.forClass(Cookie.class);
160             verify(response, times(1)).addCookie(cookieCaptor.capture());
161 
162             final Cookie cooky = cookieCaptor.getValue();
163             assertEquals(COOKIE_NAME, cooky.getName());
164             assertEquals(COOKIE_DOMAIN, cooky.getDomain());
165             assertEquals(COOKIE_PATH, cooky.getPath());
166             assertEquals(COOKIE_AGE, cooky.getMaxAge());
167             assertEquals(COOKIE_VALUE, cooky.getValue());
168         }
169     }
170 
171     private void assertThatExistingCookieIsRemoved(boolean isHttpOnly)
172     {
173         if (isHttpOnly)
174         {
175             ArgumentCaptor<String> cookieCaptor = ArgumentCaptor.forClass(String.class);
176             verify(response, times(1)).addHeader(eq("Set-Cookie"), cookieCaptor.capture());
177 
178             final String header = cookieCaptor.getValue();
179 
180             assertTrue(header.indexOf(COOKIE_NAME + "=" + "") == 0); // blank value
181 
182             assertTrue(header.indexOf("; Domain=" + COOKIE_DOMAIN) != -1);
183             assertTrue(header.indexOf("; Path=" + COOKIE_PATH) != -1);
184             assertTrue(header.indexOf("; HttpOnly") != -1);
185             // actually now its a bit hard to tell if its been immediately expired
186         }
187         else
188         {
189             ArgumentCaptor<Cookie> cookieCaptor = ArgumentCaptor.forClass(Cookie.class);
190             verify(response, times(1)).addCookie(cookieCaptor.capture());
191 
192             final Cookie cooky = cookieCaptor.getValue();
193             assertEquals(COOKIE_NAME, cooky.getName());
194             assertEquals(COOKIE_DOMAIN, cooky.getDomain());
195             assertEquals(COOKIE_PATH, cooky.getPath());
196             assertEquals(0, cooky.getMaxAge());
197             assertEquals("", cooky.getValue());
198         }
199 
200     }
201 
202     public void testGetRememberMeCookieAuthenticatedUsername_NoCookies()
203     {
204         when(request.getCookies()).thenReturn(new Cookie[0]);
205 
206         final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
207         assertNull(username);
208     }
209 
210     public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_ButItDoesNotMatch()
211     {
212         Cookie existingCookie = new Cookie(COOKIE_NAME, BAD_COOKIE_VALUE);
213         when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
214 
215         when(tokenDao.findById(TOKEN_ID)).thenReturn(fredsToken);
216 
217         final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
218         assertNull(username);
219 
220         assertThatExistingCookieIsRemoved(false);
221     }
222 
223 
224     public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_ButTheAppDoesntLikeIt()
225     {
226         Cookie existingCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
227         when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
228 
229         when(tokenDao.findById(TOKEN_ID)).thenReturn(null);
230 
231         final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
232         assertNull(username);
233 
234         assertThatExistingCookieIsRemoved(false);
235     }
236 
237     public void testGetRememberMeCookieAuthenticatedUsername_HasCookie_AndItsGood()
238     {
239         Cookie existingCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
240         when(request.getCookies()).thenReturn(new Cookie[] { existingCookie });
241 
242         when(tokenDao.findById(TOKEN_ID)).thenReturn(fredsToken);
243 
244         final String username = service.getRememberMeCookieAuthenticatedUsername(request, response);
245         assertEquals(FRED_FLINTSTONE, username);
246     }
247 }