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 removeRememberMeCookie(httpServletRequest, httpServletResponse);
57 }
58 return null;
59 }
60
61 private boolean isExpired(RememberMeToken storedToken)
62 {
63 return storedToken.getCreatedTime() + TimeUnit.SECONDS.toMillis(rememberMeConfiguration.getCookieMaxAgeInSeconds()) < System.currentTimeMillis();
64 }
65
66 public void addRememberMeCookie(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse, final String authenticatedUsername)
67 {
68 final RememberMeToken token = rememberMeTokenGenerator.generateToken(authenticatedUsername);
69 final RememberMeToken persistedToken = rememberMeTokenDao.save(token);
70
71 final String desiredCookieName = rememberMeConfiguration.getCookieName();
72 Cookie cookie = findRememberCookie(httpServletRequest, desiredCookieName);
73 if (cookie == null)
74 {
75 cookie = new Cookie(desiredCookieName, persistedToken.getRandomString());
76 }
77 setValuesIntoCookie(httpServletRequest, cookie, toCookieValue(persistedToken),
78 rememberMeConfiguration.getCookieMaxAgeInSeconds(),
79 rememberMeConfiguration.getCookieDomain(httpServletRequest),
80 rememberMeConfiguration.getCookiePath(httpServletRequest),
81 rememberMeConfiguration.isInsecureCookieAlwaysUsed()
82 );
83 setRememberMeCookie(httpServletRequest, httpServletResponse, cookie);
84 }
85
86 public void removeRememberMeCookie(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
87 {
88 final Cookie cookie = findRememberCookie(httpServletRequest, rememberMeConfiguration.getCookieName());
89 if (cookie != null)
90 {
91 RememberMeToken cookieToken = parseIntoToken(cookie);
92
93 setValuesIntoCookie(httpServletRequest, cookie, "", 0,
94 rememberMeConfiguration.getCookieDomain(httpServletRequest),
95 rememberMeConfiguration.getCookiePath(httpServletRequest),
96 rememberMeConfiguration.isInsecureCookieAlwaysUsed()
97 );
98 setRememberMeCookie(httpServletRequest, httpServletResponse, cookie);
99
100
101 if (cookieToken != null)
102 {
103 rememberMeTokenDao.remove(cookieToken.getId());
104 }
105 }
106 }
107
108 private void setValuesIntoCookie(final HttpServletRequest httpServletRequest, final Cookie cookie,
109 final String value, final int maxAgeInSeconds, final String cookieDomain,
110 final String cookiePath, final boolean isInsecureCookieUsed)
111 {
112 if (StringUtils.isNotBlank(cookieDomain))
113 {
114 cookie.setDomain(cookieDomain);
115 }
116 if (StringUtils.isNotBlank(cookiePath))
117 {
118 cookie.setPath(cookiePath);
119 }
120 if (!isInsecureCookieUsed)
121 {
122 cookie.setSecure(httpServletRequest.isSecure());
123 }
124 cookie.setMaxAge(maxAgeInSeconds);
125 cookie.setValue(escapeInvalidCookieCharacters(value));
126 }
127
128 private void setRememberMeCookie(final HttpServletRequest httpServletRequest,
129 final HttpServletResponse httpServletResponse, final Cookie cookie)
130 {
131 if (rememberMeConfiguration.isCookieHttpOnly(httpServletRequest))
132 {
133 HttpOnlyCookies.addHttpOnlyCookie(httpServletResponse, cookie);
134 }
135 else
136 {
137 httpServletResponse.addCookie(cookie);
138 }
139 }
140
141 private String toCookieValue(final RememberMeToken persistedToken)
142 {
143 return persistedToken.getId() + ":" + persistedToken.getRandomString();
144 }
145
146
147
148
149
150
151
152
153 private RememberMeToken getCookieValue(final HttpServletRequest httpServletRequest)
154 {
155 final Cookie cookie = findRememberCookie(httpServletRequest, rememberMeConfiguration.getCookieName());
156 if (cookie != null)
157 {
158 return parseIntoToken(cookie);
159 }
160 return null;
161 }
162
163 private RememberMeToken parseIntoToken(final Cookie cookie)
164 {
165 final String value = unescapeInvalidCookieCharacters(cookie.getValue());
166 if (StringUtils.isBlank(value))
167 {
168 return null;
169 }
170 int indexColon = value.indexOf(':');
171 if (indexColon <= 0 || indexColon == value.length() - 1)
172 {
173 return null;
174 }
175 Long id;
176 try
177 {
178 id = Long.parseLong(value.substring(0, indexColon));
179 }
180 catch (NumberFormatException e)
181 {
182 return null;
183 }
184 String randomString = value.substring(indexColon + 1);
185 return DefaultRememberMeToken.builder(id, randomString).build();
186 }
187
188 private Cookie findRememberCookie(HttpServletRequest httpServletRequest, final String cookieName)
189 {
190 final Cookie[] cookies = httpServletRequest.getCookies();
191 if (cookies != null)
192 {
193 for (Cookie cookie : cookies)
194 {
195 if (cookieName.equalsIgnoreCase(cookie.getName()))
196 {
197 return cookie;
198 }
199 }
200 }
201 return null;
202
203 }
204
205 private static final String URL_ENCODING = "UTF-8";
206
207
208
209
210
211
212
213
214
215
216 private static String escapeInvalidCookieCharacters(final String s)
217 {
218 try
219 {
220 return URLEncoder.encode(s, URL_ENCODING);
221 }
222 catch (final UnsupportedEncodingException e)
223 {
224 throw new AssertionError(e);
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237 private static String unescapeInvalidCookieCharacters(final String s)
238 {
239 try
240 {
241 return URLDecoder.decode(s, URL_ENCODING);
242 }
243 catch (final UnsupportedEncodingException e)
244 {
245 log.fatal("UTF-8 encoding unsupported !!?!! How is that possible in java?", e);
246 throw new AssertionError(e);
247 }
248 }
249 }