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