1 /*
2 * $Source: /usr/cvsroot/melati/melati/src/main/java/org/melati/login/HttpSessionAccessHandler.java,v $
3 * $Revision: 1.44 $
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 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 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 StringBuffer url = new StringBuffer();
104 HttpUtil.appendRelativeZoneURL(url, request);
105 url.append('/');
106 url.append(loginPageServletClassName());
107 url.append('/');
108 url.append(melati.getPoemContext().getLogicalDatabase());
109 url.append('/');
110
111 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 HttpServletRequest request = melati.getRequest();
125 HttpServletResponse response = melati.getResponse();
126 HttpSession session = request.getSession(true);
127 session.setAttribute(Login.TRIGGERING_REQUEST_PARAMETERS,
128 new HttpServletRequestParameters(request));
129 session.setAttribute(Login.TRIGGERING_EXCEPTION, accessException);
130 melati.getWriter().reset();
131 response.sendRedirect(loginPageURL(melati, request));
132 }
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 String ldb = melati.getPoemContext().getLogicalDatabase();
149 HttpSession session = melati.getSession();
150 synchronized (session) {
151 User user = (User)session.getAttribute(USER);
152 if (user == null) {
153 user = getUserFromCookie(melati,ldb);
154 if (user != null) {
155 String cookie = getCookieValue(melati,ldb+user.getLogin());
156 if (cookie == null ||
157 !cookie.equals(MD5Util.encode(user.getPassword())))
158 user = null;
159 }
160 }
161 logUsIn(melati,user);
162 }
163 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 PoemThread.setAccessToken(
175 user == null ? melati.getDatabase().guestAccessToken() : user);
176 }
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 String login = getCookieValue(melati,key);
187 if (login == null) return null;
188 return (User)melati.getDatabase().getUserTable().getLoginColumn().
189 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 key = UTF8URLEncoder.encode(key);
203 Cookie[] cookies = melati.getRequest().getCookies();
204 if(cookies == null) return null;
205 for (int i=0; i<cookies.length; i++) {
206 Cookie c = cookies[i];
207 if (c.getName().equals(key))
208 return UTF8URLEncoder.decode(c.getValue());
209 }
210 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 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 synchronized (session) {
228 HttpServletRequestParameters oldParams =
229 (HttpServletRequestParameters)session.getAttribute(OVERLAY_PARAMETERS);
230
231 if (oldParams != null) {
232 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 melati.setRequest(
238 new ReconstructedHttpServletRequest(oldParams,
239 melati.getRequest()));
240 }
241 }
242 }
243 }