1 package com.atlassian.seraph.service.rememberme;
2
3 import java.io.UnsupportedEncodingException;
4 import java.net.URLDecoder;
5 import java.net.URLEncoder;
6 import java.util.concurrent.TimeUnit;
7
8 import javax.servlet.http.Cookie;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11
12 import com.atlassian.security.cookie.HttpOnlyCookies;
13 import com.atlassian.seraph.ioc.ApplicationServicesRegistry;
14 import com.atlassian.seraph.spi.rememberme.RememberMeConfiguration;
15 import com.atlassian.seraph.spi.rememberme.RememberMeTokenDao;
16
17 import org.apache.commons.lang.StringUtils;
18 import org.apache.log4j.Logger;
19
20
21
22
23
24 public class DefaultRememberMeService implements RememberMeService
25 {
26 private static final Logger log = Logger.getLogger(DefaultRememberMeService.class);
27
28 private final RememberMeConfiguration rememberMeConfiguration;
29 private final RememberMeTokenDao rememberMeTokenDao;
30 private final RememberMeTokenGenerator rememberMeTokenGenerator;
31
32 public DefaultRememberMeService(final RememberMeConfiguration rememberMeConfiguration, final RememberMeTokenDao rememberMeTokenDao, final RememberMeTokenGenerator rememberMeTokenGenerator)
33 {
34 this.rememberMeConfiguration = rememberMeConfiguration;
35 this.rememberMeTokenDao = rememberMeTokenDao;
36 this.rememberMeTokenGenerator = rememberMeTokenGenerator;
37 ApplicationServicesRegistry.setRememberMeService(this);
38 }
39
40 public String getRememberMeCookieAuthenticatedUsername(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
41 {
42
43 RememberMeToken cookieToken = getCookieValue(httpServletRequest);
44 if (cookieToken != null)
45 {
46
47 RememberMeToken storedToken = rememberMeTokenDao.findById(cookieToken.getId());
48 if (storedToken != null)
49 {
50 if (cookieToken.getRandomString().equals(storedToken.getRandomString()) && !isExpired(storedToken))
51 {
52 return storedToken.getUserName();
53 }
54 }
55
56 if (httpServletResponse != null)
57 {
58
59 removeRememberMeCookie(httpServletRequest, httpServletResponse);
60 }
61 }
62 return null;
63 }
64
65 private boolean isExpired(RememberMeToken storedToken)
66 {
67 return storedToken.getCreatedTime() + TimeUnit.SECONDS.toMillis(rememberMeConfiguration.getCookieMaxAgeInSeconds()) < System.currentTimeMillis();
68 }
69
70 public void addRememberMeCookie(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse, final String authenticatedUsername)
71 {
72 final RememberMeToken token = rememberMeTokenGenerator.generateToken(authenticatedUsername);
73 final RememberMeToken persistedToken = rememberMeTokenDao.save(token);
74
75 final String desiredCookieName = rememberMeConfiguration.getCookieName();
76 Cookie cookie = findRememberCookie(httpServletRequest, desiredCookieName);
77 if (cookie == null)
78 {
79 cookie = new Cookie(desiredCookieName, persistedToken.getRandomString());
80 }
81 setValuesIntoCookie(httpServletRequest, cookie, toCookieValue(persistedToken),
82 rememberMeConfiguration.getCookieMaxAgeInSeconds(),
83 rememberMeConfiguration.getCookieDomain(httpServletRequest),
84 rememberMeConfiguration.getCookiePath(httpServletRequest),
85 rememberMeConfiguration.isInsecureCookieAlwaysUsed()
86 );
87 setRememberMeCookie(httpServletRequest, httpServletResponse, cookie);
88 }
89
90 public void removeRememberMeCookie(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
91 {
92 final Cookie cookie = findRememberCookie(httpServletRequest, rememberMeConfiguration.getCookieName());
93 if (cookie != null)
94 {
95 RememberMeToken cookieToken = parseIntoToken(cookie);
96
97 setValuesIntoCookie(httpServletRequest, cookie, "", 0,
98 rememberMeConfiguration.getCookieDomain(httpServletRequest),
99 rememberMeConfiguration.getCookiePath(httpServletRequest),
100 rememberMeConfiguration.isInsecureCookieAlwaysUsed()
101 );
102 setRememberMeCookie(httpServletRequest, httpServletResponse, cookie);
103
104
105 if (cookieToken != null)
106 {
107 rememberMeTokenDao.remove(cookieToken.getId());
108 }
109 }
110 }
111
112 private void setValuesIntoCookie(final HttpServletRequest httpServletRequest, final Cookie cookie,
113 final String value, final int maxAgeInSeconds, final String cookieDomain,
114 final String cookiePath, final boolean isInsecureCookieUsed)
115 {
116 if (StringUtils.isNotBlank(cookieDomain))
117 {
118 cookie.setDomain(cookieDomain);
119 }
120 if (StringUtils.isNotBlank(cookiePath))
121 {
122 cookie.setPath(cookiePath);
123 }
124 if (!isInsecureCookieUsed)
125 {
126 cookie.setSecure(httpServletRequest.isSecure());
127 }
128 cookie.setMaxAge(maxAgeInSeconds);
129 cookie.setValue(escapeInvalidCookieCharacters(value));
130 }
131
132 private void setRememberMeCookie(final HttpServletRequest httpServletRequest,
133 final HttpServletResponse httpServletResponse, final Cookie cookie)
134 {
135 if (rememberMeConfiguration.isCookieHttpOnly(httpServletRequest))
136 {
137 HttpOnlyCookies.addHttpOnlyCookie(httpServletResponse, cookie);
138 }
139 else
140 {
141 httpServletResponse.addCookie(cookie);
142 }
143 }
144
145 private String toCookieValue(final RememberMeToken persistedToken)
146 {
147 return persistedToken.getId() + ":" + persistedToken.getRandomString();
148 }
149
150
151
152
153
154
155
156
157 private RememberMeToken getCookieValue(final HttpServletRequest httpServletRequest)
158 {
159 final Cookie cookie = findRememberCookie(httpServletRequest, rememberMeConfiguration.getCookieName());
160 if (cookie != null)
161 {
162 return parseIntoToken(cookie);
163 }
164 return null;
165 }
166
167 private RememberMeToken parseIntoToken(final Cookie cookie)
168 {
169 final String value = unescapeInvalidCookieCharacters(cookie.getValue());
170 if (StringUtils.isBlank(value))
171 {
172 return null;
173 }
174 int indexColon = value.indexOf(':');
175 if (indexColon <= 0 || indexColon == value.length() - 1)
176 {
177 return null;
178 }
179 Long id;
180 try
181 {
182 id = Long.parseLong(value.substring(0, indexColon));
183 }
184 catch (NumberFormatException e)
185 {
186 return null;
187 }
188 String randomString = value.substring(indexColon + 1);
189 return DefaultRememberMeToken.builder(id, randomString).build();
190 }
191
192 private Cookie findRememberCookie(HttpServletRequest httpServletRequest, final String cookieName)
193 {
194 final Cookie[] cookies = httpServletRequest.getCookies();
195 if (cookies != null)
196 {
197 for (Cookie cookie : cookies)
198 {
199 if (cookieName.equalsIgnoreCase(cookie.getName()))
200 {
201 return cookie;
202 }
203 }
204 }
205 return null;
206
207 }
208
209 private static final String URL_ENCODING = "UTF-8";
210
211
212
213
214
215
216
217
218
219
220 private static String escapeInvalidCookieCharacters(final String s)
221 {
222 try
223 {
224 return URLEncoder.encode(s, URL_ENCODING);
225 }
226 catch (final UnsupportedEncodingException e)
227 {
228 throw new AssertionError(e);
229 }
230 }
231
232
233
234
235
236
237
238
239
240
241 private static String unescapeInvalidCookieCharacters(final String s)
242 {
243 try
244 {
245 return URLDecoder.decode(s, URL_ENCODING);
246 }
247 catch (final UnsupportedEncodingException e)
248 {
249 log.fatal("UTF-8 encoding unsupported !!?!! How is that possible in java?", e);
250 throw new AssertionError(e);
251 }
252 }
253 }