Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
HttpSessionAccessHandler |
|
| 2.625;2.625 |
1 | /* | |
2 | * $Source$ | |
3 | * $Revision$ | |
4 | * | |
5 | * Copyright (C) 2000 William Chesters | |
6 | * | |
7 | * Part of Melati (http://melati.org), a framework for the rapid | |
8 | * development of clean, maintainable web applications. | |
9 | * | |
10 | * Melati is free software; Permission is granted to copy, distribute | |
11 | * and/or modify this software under the terms either: | |
12 | * | |
13 | * a) the GNU General Public License as published by the Free Software | |
14 | * Foundation; either version 2 of the License, or (at your option) | |
15 | * any later version, | |
16 | * | |
17 | * or | |
18 | * | |
19 | * b) any version of the Melati Software License, as published | |
20 | * at http://melati.org | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License and | |
23 | * the Melati Software License along with this program; | |
24 | * if not, write to the Free Software Foundation, Inc., | |
25 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the | |
26 | * GNU General Public License and visit http://melati.org to obtain the | |
27 | * Melati Software License. | |
28 | * | |
29 | * Feel free to contact the Developers of Melati (http://melati.org), | |
30 | * if you would like to work out a different arrangement than the options | |
31 | * outlined here. It is our intention to allow Melati to be used by as | |
32 | * wide an audience as possible. | |
33 | * | |
34 | * This program is distributed in the hope that it will be useful, | |
35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
37 | * GNU General Public License for more details. | |
38 | * | |
39 | * Contact details for copyright holder: | |
40 | * | |
41 | * William Chesters <williamc At paneris.org> | |
42 | * http://paneris.org/~williamc | |
43 | * Obrechtstraat 114, 2517VX Den Haag, The Netherlands | |
44 | */ | |
45 | ||
46 | package org.melati.login; | |
47 | ||
48 | ||
49 | import javax.servlet.http.Cookie; | |
50 | import javax.servlet.http.HttpServletRequest; | |
51 | import javax.servlet.http.HttpServletResponse; | |
52 | import javax.servlet.http.HttpSession; | |
53 | ||
54 | import org.melati.Melati; | |
55 | import org.melati.poem.AccessPoemException; | |
56 | import org.melati.poem.PoemThread; | |
57 | import org.melati.poem.User; | |
58 | import org.melati.util.HttpServletRequestParameters; | |
59 | import org.melati.util.HttpUtil; | |
60 | import org.melati.util.MD5Util; | |
61 | import org.melati.util.ReconstructedHttpServletRequest; | |
62 | import org.melati.util.ReconstructedHttpServletRequestMismatchException; | |
63 | import org.melati.util.UTF8URLEncoder; | |
64 | ||
65 | /** | |
66 | * An {@link AccessHandler} which uses <code>Session</code> cookies to | |
67 | * elicit and maintain the user's login and password. | |
68 | */ | |
69 | 5 | public class HttpSessionAccessHandler implements AccessHandler { |
70 | ||
71 | /** Class name. */ | |
72 | public static final String | |
73 | OVERLAY_PARAMETERS = | |
74 | "org.melati.login.HttpSessionAccessHandler.overlayParameters"; | |
75 | /** Class name. */ | |
76 | public static final String | |
77 | USER = | |
78 | "org.melati.login.HttpSessionAccessHandler.user"; | |
79 | ||
80 | /** | |
81 | * The class name of the class implementing the login servlet. Unless | |
82 | * overridden, this is <TT>org.melati.login.Login</TT>. | |
83 | * | |
84 | * @return the class name of login servlet | |
85 | * @see org.melati.login.Login | |
86 | */ | |
87 | protected String loginPageServletClassName() { | |
88 | 7 | return "org.melati.login.Login"; |
89 | } | |
90 | ||
91 | /** | |
92 | * The URL of the login servlet. Unless overridden, this is computed by | |
93 | * substituting {@link #loginPageServletClassName()} into the URL of the | |
94 | * request being serviced. | |
95 | * | |
96 | * @param melati the current Melati | |
97 | * @param request the request currently being serviced | |
98 | * | |
99 | * @return the login page url | |
100 | * @see #loginPageServletClassName | |
101 | */ | |
102 | public String loginPageURL(Melati melati, HttpServletRequest request) { | |
103 | 7 | StringBuffer url = new StringBuffer(); |
104 | 7 | HttpUtil.appendRelativeZoneURL(url, request); |
105 | 7 | url.append('/'); |
106 | 7 | url.append(loginPageServletClassName()); |
107 | 7 | url.append('/'); |
108 | 7 | url.append(melati.getPoemContext().getLogicalDatabase()); |
109 | 7 | url.append('/'); |
110 | ||
111 | 7 | return url.toString(); |
112 | } | |
113 | ||
114 | ||
115 | /** | |
116 | * Store the current request and redirect to the login page. | |
117 | * | |
118 | * {@inheritDoc} | |
119 | * @see org.melati.login.AccessHandler#handleAccessException | |
120 | */ | |
121 | public void handleAccessException(Melati melati, | |
122 | AccessPoemException accessException) | |
123 | throws Exception { | |
124 | 7 | HttpServletRequest request = melati.getRequest(); |
125 | 7 | HttpServletResponse response = melati.getResponse(); |
126 | 7 | HttpSession session = request.getSession(true); |
127 | 7 | session.setAttribute(Login.TRIGGERING_REQUEST_PARAMETERS, |
128 | new HttpServletRequestParameters(request)); | |
129 | 7 | session.setAttribute(Login.TRIGGERING_EXCEPTION, accessException); |
130 | 7 | melati.getWriter().reset(); |
131 | 7 | response.sendRedirect(loginPageURL(melati, request)); |
132 | 7 | } |
133 | ||
134 | /** | |
135 | * Set the Access token to be used for this request. | |
136 | * | |
137 | * The Access Token is either picked up from the session, or from a cookie. | |
138 | * The cookie is keyed on the logical database and is used to | |
139 | * retrieve the user's login. | |
140 | * The login is used (with the logical database name) to retrieve an encoded | |
141 | * password which is then checked. | |
142 | * | |
143 | * {@inheritDoc} | |
144 | * @see org.melati.login.AccessHandler#establishUser(org.melati.Melati) | |
145 | */ | |
146 | public Melati establishUser(Melati melati) { | |
147 | // now when we establish a user, we must also set the cookie | |
148 | 511 | String ldb = melati.getPoemContext().getLogicalDatabase(); |
149 | 511 | HttpSession session = melati.getSession(); |
150 | 511 | synchronized (session) { |
151 | 511 | User user = (User)session.getAttribute(USER); |
152 | 511 | if (user == null) { |
153 | 274 | user = getUserFromCookie(melati,ldb); |
154 | 274 | if (user != null) { |
155 | 5 | String cookie = getCookieValue(melati,ldb+user.getLogin()); |
156 | 5 | if (cookie == null || |
157 | 5 | !cookie.equals(MD5Util.encode(user.getPassword()))) |
158 | 0 | user = null; |
159 | } | |
160 | } | |
161 | 511 | logUsIn(melati,user); |
162 | 511 | } |
163 | 511 | return melati; |
164 | } | |
165 | ||
166 | /** | |
167 | * Set our AccessToken. | |
168 | * NOTE Remember a User isa Token. | |
169 | * | |
170 | * @param melati the Melati to get our database from | |
171 | * @param user the token to set | |
172 | */ | |
173 | protected void logUsIn(Melati melati, User user) { | |
174 | 1022 | PoemThread.setAccessToken( |
175 | 269 | user == null ? melati.getDatabase().guestAccessToken() : user); |
176 | 511 | } |
177 | ||
178 | ||
179 | /** | |
180 | * Extract User via the cookie. | |
181 | * @param melati our Melati | |
182 | * @param key cookie key | |
183 | * @return the found User or null | |
184 | */ | |
185 | User getUserFromCookie(Melati melati,String key) { | |
186 | 274 | String login = getCookieValue(melati,key); |
187 | 274 | if (login == null) return null; |
188 | 5 | return (User)melati.getDatabase().getUserTable().getLoginColumn(). |
189 | 5 | firstWhereEq(login); |
190 | } | |
191 | ||
192 | /** | |
193 | * Extract a value from the cookies. | |
194 | * | |
195 | * @param melati the Melati in which the Request and its cookies are stored | |
196 | * @param key the key we need the value of | |
197 | * @return the cookie value or null | |
198 | */ | |
199 | String getCookieValue(Melati melati,String key) { | |
200 | // try and get from cookie | |
201 | // Use default encoding, regardless of user's encoding | |
202 | 279 | key = UTF8URLEncoder.encode(key); |
203 | 279 | Cookie[] cookies = melati.getRequest().getCookies(); |
204 | 279 | if(cookies == null) return null; |
205 | 405 | for (int i=0; i<cookies.length; i++) { |
206 | 215 | Cookie c = cookies[i]; |
207 | 215 | if (c.getName().equals(key)) |
208 | 10 | return UTF8URLEncoder.decode(c.getValue()); |
209 | } | |
210 | 190 | return null; |
211 | } | |
212 | ||
213 | /** | |
214 | * If we are returning from a login rebuild the original request, | |
215 | * otherwise do nothing. | |
216 | * | |
217 | * {@inheritDoc} | |
218 | * @see org.melati.login.AccessHandler#buildRequest(org.melati.Melati) | |
219 | */ | |
220 | public void buildRequest(Melati melati) | |
221 | throws ReconstructedHttpServletRequestMismatchException { | |
222 | 513 | HttpSession session = melati.getSession(); |
223 | ||
224 | // First off, is the user continuing after a login? If so, we want to | |
225 | // recover any POSTed fields from the request that triggered it. | |
226 | ||
227 | 513 | synchronized (session) { |
228 | 513 | HttpServletRequestParameters oldParams = |
229 | 513 | (HttpServletRequestParameters)session.getAttribute(OVERLAY_PARAMETERS); |
230 | ||
231 | 513 | if (oldParams != null) { |
232 | 2 | session.removeAttribute(OVERLAY_PARAMETERS); |
233 | ||
234 | // we don't want to create a new object here, rather we are simply | |
235 | // going to set up the old request parameters | |
236 | ||
237 | 4 | melati.setRequest( |
238 | new ReconstructedHttpServletRequest(oldParams, | |
239 | 2 | melati.getRequest())); |
240 | } | |
241 | 513 | } |
242 | 513 | } |
243 | } |