001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/FormAuthenticator.java,v 1.20 2002/03/14 20:58:24 remm Exp $
003: * $Revision: 1.20 $
004: * $Date: 2002/03/14 20:58:24 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.authenticator;
065:
066: import java.io.IOException;
067: import java.security.Principal;
068: import java.util.Enumeration;
069: import java.util.Iterator;
070: import java.util.Locale;
071: import java.util.Map;
072: import javax.servlet.http.Cookie;
073: import javax.servlet.http.HttpServletRequest;
074: import javax.servlet.http.HttpServletResponse;
075: import javax.servlet.http.HttpSession;
076: import org.apache.catalina.HttpRequest;
077: import org.apache.catalina.HttpResponse;
078: import org.apache.catalina.Realm;
079: import org.apache.catalina.Session;
080: import org.apache.catalina.deploy.LoginConfig;
081:
082: /**
083: * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
084: * Authentication, as described in the Servlet API Specification, Version 2.2.
085: *
086: * @author Craig R. McClanahan
087: * @version $Revision: 1.20 $ $Date: 2002/03/14 20:58:24 $
088: */
089:
090: public class FormAuthenticator extends AuthenticatorBase {
091:
092: // ----------------------------------------------------- Instance Variables
093:
094: /**
095: * Descriptive information about this implementation.
096: */
097: protected static final String info = "org.apache.catalina.authenticator.FormAuthenticator/1.0";
098:
099: // ------------------------------------------------------------- Properties
100:
101: /**
102: * Return descriptive information about this Valve implementation.
103: */
104: public String getInfo() {
105:
106: return (this .info);
107:
108: }
109:
110: // --------------------------------------------------------- Public Methods
111:
112: /**
113: * Authenticate the user making this request, based on the specified
114: * login configuration. Return <code>true</code> if any specified
115: * constraint has been satisfied, or <code>false</code> if we have
116: * created a response challenge already.
117: *
118: * @param request Request we are processing
119: * @param response Response we are creating
120: * @param login Login configuration describing how authentication
121: * should be performed
122: *
123: * @exception IOException if an input/output error occurs
124: */
125: public boolean authenticate(HttpRequest request,
126: HttpResponse response, LoginConfig config)
127: throws IOException {
128:
129: // References to objects we will need later
130: HttpServletRequest hreq = (HttpServletRequest) request
131: .getRequest();
132: HttpServletResponse hres = (HttpServletResponse) response
133: .getResponse();
134: Session session = null;
135:
136: // Have we already authenticated someone?
137: Principal principal = hreq.getUserPrincipal();
138: if (principal != null) {
139: if (debug >= 1)
140: log("Already authenticated '" + principal.getName()
141: + "'");
142: String ssoId = (String) request
143: .getNote(Constants.REQ_SSOID_NOTE);
144: if (ssoId != null)
145: associate(ssoId, getSession(request, true));
146: return (true);
147: }
148:
149: // Have we authenticated this user before but have caching disabled?
150: if (!cache) {
151: session = getSession(request, true);
152: if (debug >= 1)
153: log("Checking for reauthenticate in session " + session);
154: String username = (String) session
155: .getNote(Constants.SESS_USERNAME_NOTE);
156: String password = (String) session
157: .getNote(Constants.SESS_PASSWORD_NOTE);
158: if ((username != null) && (password != null)) {
159: if (debug >= 1)
160: log("Reauthenticating username '" + username + "'");
161: principal = context.getRealm().authenticate(username,
162: password);
163: if (principal != null) {
164: session.setNote(Constants.FORM_PRINCIPAL_NOTE,
165: principal);
166: register(request, response, principal,
167: Constants.FORM_METHOD, username, password);
168: return (true);
169: }
170: if (debug >= 1)
171: log("Reauthentication failed, proceed normally");
172: }
173: }
174:
175: // Is this the re-submit of the original request URI after successful
176: // authentication? If so, forward the *original* request instead.
177: if (matchRequest(request)) {
178: session = getSession(request, true);
179: if (debug >= 1)
180: log("Restore request from session '" + session.getId()
181: + "'");
182: principal = (Principal) session
183: .getNote(Constants.FORM_PRINCIPAL_NOTE);
184: register(request, response, principal,
185: Constants.FORM_METHOD, (String) session
186: .getNote(Constants.SESS_USERNAME_NOTE),
187: (String) session
188: .getNote(Constants.SESS_PASSWORD_NOTE));
189: String ssoId = (String) request
190: .getNote(Constants.REQ_SSOID_NOTE);
191: if (ssoId != null)
192: associate(ssoId, session);
193: if (restoreRequest(request, session)) {
194: if (debug >= 1)
195: log("Proceed to restored request");
196: return (true);
197: } else {
198: if (debug >= 1)
199: log("Restore of original request failed");
200: hres.sendError(HttpServletResponse.SC_BAD_REQUEST);
201: return (false);
202: }
203: }
204:
205: // Acquire references to objects we will need to evaluate
206: String contextPath = hreq.getContextPath();
207: String requestURI = request.getDecodedRequestURI();
208: response.setContext(request.getContext());
209:
210: // Is this a request for the login page itself? Test here to avoid
211: // displaying it twice (from the user's perspective) -- once because
212: // of the "save and redirect" and once because of the "restore and
213: // redirect" performed below.
214: String loginURI = contextPath + config.getLoginPage();
215: if (requestURI.equals(loginURI)) {
216: if (debug >= 1)
217: log("Requesting login page normally");
218: return (true); // Display the login page in the usual manner
219: }
220:
221: // Is this a request for the error page itself? Test here to avoid
222: // an endless loop (back to the login page) if the error page is
223: // within the protected area of our security constraint
224: String errorURI = contextPath + config.getErrorPage();
225: if (requestURI.equals(errorURI)) {
226: if (debug >= 1)
227: log("Requesting error page normally");
228: return (true); // Display the error page in the usual manner
229: }
230:
231: // Is this the action request from the login page?
232: boolean loginAction = requestURI.startsWith(contextPath)
233: && requestURI.endsWith(Constants.FORM_ACTION);
234:
235: // No -- Save this request and redirect to the form login page
236: if (!loginAction) {
237: session = getSession(request, true);
238: if (debug >= 1)
239: log("Save request in session '" + session.getId() + "'");
240: saveRequest(request, session);
241: if (debug >= 1)
242: log("Redirect to login page '" + loginURI + "'");
243: hres.sendRedirect(hres.encodeRedirectURL(loginURI));
244: return (false);
245: }
246:
247: // Yes -- Validate the specified credentials and redirect
248: // to the error page if they are not correct
249: Realm realm = context.getRealm();
250: String username = hreq.getParameter(Constants.FORM_USERNAME);
251: String password = hreq.getParameter(Constants.FORM_PASSWORD);
252: if (debug >= 1)
253: log("Authenticating username '" + username + "'");
254: principal = realm.authenticate(username, password);
255: if (principal == null) {
256: if (debug >= 1)
257: log("Redirect to error page '" + errorURI + "'");
258: hres.sendRedirect(hres.encodeRedirectURL(errorURI));
259: return (false);
260: }
261:
262: // Save the authenticated Principal in our session
263: if (debug >= 1)
264: log("Authentication of '" + username + "' was successful");
265: if (session == null)
266: session = getSession(request, true);
267: session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
268:
269: // If we are not caching, save the username and password as well
270: if (!cache) {
271: session.setNote(Constants.SESS_USERNAME_NOTE, username);
272: session.setNote(Constants.SESS_PASSWORD_NOTE, password);
273: }
274:
275: // Redirect the user to the original request URI (which will cause
276: // the original request to be restored)
277: requestURI = savedRequestURL(session);
278: if (debug >= 1)
279: log("Redirecting to original '" + requestURI + "'");
280: if (requestURI == null)
281: hres.sendError(HttpServletResponse.SC_BAD_REQUEST, sm
282: .getString("authenticator.formlogin"));
283: else
284: hres.sendRedirect(hres.encodeRedirectURL(requestURI));
285: return (false);
286:
287: }
288:
289: // ------------------------------------------------------ Protected Methods
290:
291: /**
292: * Does this request match the saved one (so that it must be the redirect
293: * we signalled after successful authentication?
294: *
295: * @param request The request to be verified
296: */
297: protected boolean matchRequest(HttpRequest request) {
298:
299: // Has a session been created?
300: Session session = getSession(request, false);
301: if (session == null)
302: return (false);
303:
304: // Is there a saved request?
305: SavedRequest sreq = (SavedRequest) session
306: .getNote(Constants.FORM_REQUEST_NOTE);
307: if (sreq == null)
308: return (false);
309:
310: // Is there a saved principal?
311: if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null)
312: return (false);
313:
314: // Does the request URI match?
315: HttpServletRequest hreq = (HttpServletRequest) request
316: .getRequest();
317: String requestURI = hreq.getRequestURI();
318: if (requestURI == null)
319: return (false);
320: return (requestURI.equals(sreq.getRequestURI()));
321:
322: }
323:
324: /**
325: * Restore the original request from information stored in our session.
326: * If the original request is no longer present (because the session
327: * timed out), return <code>false</code>; otherwise, return
328: * <code>true</code>.
329: *
330: * @param request The request to be restored
331: * @param session The session containing the saved information
332: */
333: protected boolean restoreRequest(HttpRequest request,
334: Session session) {
335:
336: // Retrieve and remove the SavedRequest object from our session
337: SavedRequest saved = (SavedRequest) session
338: .getNote(Constants.FORM_REQUEST_NOTE);
339: session.removeNote(Constants.FORM_REQUEST_NOTE);
340: session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
341: if (saved == null)
342: return (false);
343:
344: // Modify our current request to reflect the original one
345: request.clearCookies();
346: Iterator cookies = saved.getCookies();
347: while (cookies.hasNext()) {
348: request.addCookie((Cookie) cookies.next());
349: }
350: request.clearHeaders();
351: Iterator names = saved.getHeaderNames();
352: while (names.hasNext()) {
353: String name = (String) names.next();
354: Iterator values = saved.getHeaderValues(name);
355: while (values.hasNext()) {
356: request.addHeader(name, (String) values.next());
357: }
358: }
359: request.clearLocales();
360: Iterator locales = saved.getLocales();
361: while (locales.hasNext()) {
362: request.addLocale((Locale) locales.next());
363: }
364: request.clearParameters();
365: if ("POST".equalsIgnoreCase(saved.getMethod())) {
366: Iterator paramNames = saved.getParameterNames();
367: while (paramNames.hasNext()) {
368: String paramName = (String) paramNames.next();
369: String paramValues[] = (String[]) saved
370: .getParameterValues(paramName);
371: request.addParameter(paramName, paramValues);
372: }
373: }
374: request.setMethod(saved.getMethod());
375: request.setQueryString(saved.getQueryString());
376: request.setRequestURI(saved.getRequestURI());
377: return (true);
378:
379: }
380:
381: /**
382: * Save the original request information into our session.
383: *
384: * @param request The request to be saved
385: * @param session The session to contain the saved information
386: */
387: private void saveRequest(HttpRequest request, Session session) {
388:
389: // Create and populate a SavedRequest object for this request
390: HttpServletRequest hreq = (HttpServletRequest) request
391: .getRequest();
392: SavedRequest saved = new SavedRequest();
393: Cookie cookies[] = hreq.getCookies();
394: if (cookies != null) {
395: for (int i = 0; i < cookies.length; i++)
396: saved.addCookie(cookies[i]);
397: }
398: Enumeration names = hreq.getHeaderNames();
399: while (names.hasMoreElements()) {
400: String name = (String) names.nextElement();
401: Enumeration values = hreq.getHeaders(name);
402: while (values.hasMoreElements()) {
403: String value = (String) values.nextElement();
404: saved.addHeader(name, value);
405: }
406: }
407: Enumeration locales = hreq.getLocales();
408: while (locales.hasMoreElements()) {
409: Locale locale = (Locale) locales.nextElement();
410: saved.addLocale(locale);
411: }
412: Map parameters = hreq.getParameterMap();
413: Iterator paramNames = parameters.keySet().iterator();
414: while (paramNames.hasNext()) {
415: String paramName = (String) paramNames.next();
416: String paramValues[] = (String[]) parameters.get(paramName);
417: saved.addParameter(paramName, paramValues);
418: }
419: saved.setMethod(hreq.getMethod());
420: saved.setQueryString(hreq.getQueryString());
421: saved.setRequestURI(hreq.getRequestURI());
422:
423: // Stash the SavedRequest in our session for later use
424: session.setNote(Constants.FORM_REQUEST_NOTE, saved);
425:
426: }
427:
428: /**
429: * Return the request URI (with the corresponding query string, if any)
430: * from the saved request so that we can redirect to it.
431: *
432: * @param session Our current session
433: */
434: private String savedRequestURL(Session session) {
435:
436: SavedRequest saved = (SavedRequest) session
437: .getNote(Constants.FORM_REQUEST_NOTE);
438: if (saved == null)
439: return (null);
440: StringBuffer sb = new StringBuffer(saved.getRequestURI());
441: if (saved.getQueryString() != null) {
442: sb.append('?');
443: sb.append(saved.getQueryString());
444: }
445: return (sb.toString());
446:
447: }
448:
449: }
|