001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.core.servlet;
066:
067: import com.jcorporate.expresso.core.controller.NonHandleableException;
068: import com.jcorporate.expresso.core.db.DBException;
069: import com.jcorporate.expresso.core.i18n.Messages;
070: import com.jcorporate.expresso.core.jsdkapi.GenericSession;
071: import com.jcorporate.expresso.core.logging.LogException;
072: import com.jcorporate.expresso.core.misc.ConfigManager;
073: import com.jcorporate.expresso.core.misc.CookieUtil;
074: import com.jcorporate.expresso.core.misc.CurrentLogin;
075: import com.jcorporate.expresso.core.misc.StringUtil;
076: import com.jcorporate.expresso.core.misc.SystemMacros;
077: import com.jcorporate.expresso.core.registry.MutableRequestRegistry;
078: import com.jcorporate.expresso.core.security.User;
079: import org.apache.log4j.Logger;
080:
081: import javax.servlet.ServletConfig;
082: import javax.servlet.ServletException;
083: import javax.servlet.http.Cookie;
084: import javax.servlet.http.HttpServletRequest;
085: import javax.servlet.http.HttpServletResponse;
086: import java.util.Enumeration;
087:
088: /**
089: * CheckLogin accepts an HttpServletRequest and HttpServletResponse object
090: * pair, and attempts to check if the user is logged in.
091: * If not, CheckLogin tries to log the user in via a cookie from the
092: * client. If this is not possible, the user is logged in as "NONE".
093: * <p/>
094: * It has now been modified to be a Singleton Object rather than having a new
095: * object allocated with each request.
096: * <p/>
097: * Example Usage:<br />
098: * <code>CheckLogin.getInstance().checkLogin(request,response);</code><br />
099: *
100: * @author Michael Nash, Singleton Modification by Michael Rimov
101: */
102: final public class CheckLogin {
103: private static Logger log = Logger.getLogger(CheckLogin.class);
104: static CheckLogin theInstance = new CheckLogin();
105:
106: /**
107: * Default Constructor.... Simply sets up the Log It should not be called
108: * anymore directly. Use getInstance() instead.
109: */
110: protected CheckLogin() {
111: }
112:
113: public static CheckLogin getInstance() {
114: return theInstance;
115: }
116:
117: /**
118: * see if login is legitimate
119: *
120: * @param request Standard request object
121: * @param response Standard response object
122: * @param c ServletConfig object of the calling servlet
123: * @param forceDB the db to force a login to
124: * @throws ServletException If an uncaught exception occurs
125: * @throws NonHandleableException upon fatal error
126: * @deprecated use other checkLogin(request, forcedb); 9/04 v.5.5+
127: */
128: public void checkLogin(HttpServletRequest request,
129: HttpServletResponse response, ServletConfig c,
130: String forceDB) throws ServletException,
131: NonHandleableException {
132: if (request == null) {
133: throw new ServletException("No request - cannot log in");
134: }
135:
136: check(request, forceDB);
137: } /* CheckLogin(HttpServletRequest, HttpServletResponse, ServletConfig) */
138:
139: /**
140: * see if login is legitimate
141: *
142: * @param request Standard request object
143: * @param forceDB the db to force a login to
144: * @throws ServletException If an uncaught exception occurs
145: * @throws NonHandleableException upon fatal error
146: */
147: public void checkLogin(HttpServletRequest request, String forceDB)
148: throws ServletException, NonHandleableException {
149: if (request == null) {
150: throw new ServletException(
151: "No request, response or ServletConfig "
152: + "- cannot log in");
153: }
154:
155: check(request, forceDB);
156: }
157:
158: /**
159: * see if login is legitimate
160: *
161: * @param request Standard request object
162: * @throws ServletException If an uncaught exception occurs
163: * @throws NonHandleableException upon fatal error
164: */
165: public void checkLogin(HttpServletRequest request)
166: throws ServletException, NonHandleableException {
167: if (request == null) {
168: throw new ServletException(
169: "No request, response or ServletConfig "
170: + "- cannot log in");
171: }
172:
173: check(request, null);
174: }
175:
176: /**
177: * see if login is legitimate
178: *
179: * @param request Standard request object
180: * @param response Standard response object
181: * @param c ServletConfig object of the calling servlet
182: * @throws ServletException If an uncaught exception occurs
183: * @throws NonHandleableException upon fatal error
184: * @deprecated use other checkLogin(request, forcedb); 9/04 v.5.5+
185: */
186: public void checkLogin(HttpServletRequest request,
187: HttpServletResponse response, ServletConfig c)
188: throws ServletException, NonHandleableException {
189: if (request == null) {
190: throw new ServletException(
191: "No request, response or ServletConfig "
192: + "- cannot log in");
193: }
194:
195: check(request, null);
196: } /* CheckLogin(HttpServletRequest, HttpServletResponse, ServletConfig) */
197:
198: /**
199: * see if login is legitimate
200: *
201: * @param request Standard request object
202: * @param response Standard response object
203: * @throws NonHandleableException upon fatal error
204: * @deprecated use other checkLogin(request, forcedb); 9/04 v.5.5+
205: */
206: public void checkLogin(HttpServletRequest request,
207: HttpServletResponse response) throws NonHandleableException {
208: if (request == null) {
209: if (log.isDebugEnabled()) {
210: log.debug("No request - cannot checklogin");
211: }
212:
213: return;
214: }
215:
216: check(request, null);
217: } /* CheckLogin(HttpServletRequest, HttpServletResponse) */
218:
219: /**
220: * actaully do the work of checking login
221: *
222: * @param request The servlet request object
223: * @param forceDB the db to force a login to
224: * @throws NonHandleableException upon fatal error
225: */
226: private void check(HttpServletRequest request, String forceDB)
227: throws NonHandleableException {
228: try {
229:
230: if (SystemMacros.getInstance().getServerPrefix() == null) {
231: SystemMacros.getInstance().setServerPrefix(
232: request.getServerName() + ":"
233: + request.getServerPort());
234: }
235:
236: ConfigManager.setRequest(request);
237: Object o = GenericSession.getAttribute(request,
238: "CurrentLogin");
239: String currentUserName = User.UNKNOWN_USER;
240: String currentDB = "";
241: CurrentLogin cl = null;
242:
243: if (o != null) {
244: cl = (CurrentLogin) o;
245: currentUserName = cl.getUserName();
246: currentDB = cl.getDBName();
247:
248: //There should never be UID zero. So if there is, treat it
249: //as user NONE.
250: if (cl.getUid() == 0) {
251: if (log.isDebugEnabled()) {
252: log.debug("Got uid 0 for current login: "
253: + cl.toString());
254: }
255: logInAsNone(request, forceDB);
256: }
257:
258: if (log.isDebugEnabled()) {
259: log.debug("UserName '" + currentUserName
260: + "', db '" + currentDB + "' in session");
261: }
262:
263: boolean isInSesson = false;
264: if (forceDB == null) {
265: isInSesson = true;
266: } else if (forceDB.equals(currentDB)) {
267: isInSesson = true;
268: }
269:
270: if (isInSesson) {
271: User user = new User();
272: user.setDBName(currentDB);
273: user.setUid(cl.getUid());
274: if (user.find()) {
275: //The following line sets the particular instance of requestRegistry
276: //into the threadlocal context.
277: new MutableRequestRegistry(currentDB, user);
278: return;
279: }
280: }
281: }
282:
283: if (log.isDebugEnabled()) {
284: log
285: .debug("No login in current session (or in wrong db)");
286: }
287:
288: if (loginViaContainer(request, forceDB)) {
289: return;
290: }
291:
292: if (loginViaCookie(request, forceDB)) {
293: return;
294: }
295:
296: if (log.isDebugEnabled()) {
297: log
298: .debug("No other login methods completed logging in as NONE");
299: }
300: logInAsNone(request, forceDB);
301: } catch (Exception de) {
302: de.printStackTrace();
303: log.error(de);
304: throw new NonHandleableException(de);
305: }
306: } /* init(HttpServletRequest, HttpServletResponse) */
307:
308: /**
309: * For some reason a login session could not be established,
310: * so log the user in as the "unknown" user "NONE"
311: *
312: * @param request the servlet request object
313: * @param forceDB the data context to log into
314: */
315: public void logInAsNone(HttpServletRequest request, String forceDB)
316: throws ServletException {
317:
318: /* If no db is established, set as default */
319: String dbToLogin = "default";
320:
321: if (forceDB != null) {
322: dbToLogin = forceDB;
323: }
324:
325: int uid = 0;
326:
327: User userNone = null;
328: try {
329: userNone = new User();
330: userNone.setDataContext(dbToLogin);
331: userNone.setLoginName(User.UNKNOWN_USER);
332:
333: if (userNone.find()) {
334: uid = userNone.getUid();
335: }
336: } catch (DBException de) {
337: log.error(de);
338: }
339:
340: ConfigManager.removeSession(GenericSession.getId(request));
341:
342: CurrentLogin myLogin = CurrentLogin.newInstance(
343: User.UNKNOWN_USER, request.getRemoteAddr(), dbToLogin,
344: uid);
345: GenericSession.setAttribute(request, "CurrentLogin", myLogin);
346:
347: //The following line sets the particular instance of requestRegistry
348: //into the threadlocal context.
349: new MutableRequestRegistry(dbToLogin, userNone);
350:
351: Messages.establishLocale(request);
352: } /* logInAsNone(HttpServletRequest) */
353:
354: /**
355: * Try to log in with a user name obtained from the container. This
356: * function assumes that the container has already authenticated the user's
357: * ID and password, thus no password checking is performed.
358: * <p/>
359: * This is intended to allow more fine-grained access control via Expresso's
360: * built-in mechanisms.
361: * <p/>
362: * If successful, return true. If not, return false.
363: *
364: * @param request The request object
365: * @param forceDB Name of default database to set
366: * @return true if successfull
367: * @throws Exception upon error
368: */
369: public boolean loginViaContainer(HttpServletRequest request,
370: String forceDB) throws Exception {
371:
372: /* Obtain user name from container. Abort if null. */
373: String userName = request.getRemoteUser();
374: if (userName == null) {
375: return false;
376: }
377:
378: /* If no db is established, set to default */
379: String dbToLogin = "default";
380:
381: if (forceDB != null) {
382: dbToLogin = forceDB;
383: }
384:
385: User this User = new User();
386: this User.setDataContext(dbToLogin);
387: this User.setLoginName(userName);
388:
389: if (!this User.find()) {
390: return false;
391: }
392:
393: if (!this User.getAccountStatus().equals("A")) {
394: throw new ServletException(
395: "Access denied: Expresso account '" + userName
396: + "' is not active.");
397: }
398:
399: if (log.isInfoEnabled()) {
400: log.info("User " + this User.getDisplayName()
401: + " logged in via the container");
402: }
403:
404: doSuccessfulAuth(request, userName, dbToLogin, this User);
405: return true;
406: }
407:
408: /**
409: * Try to log in via the cookie from the client - if successful, return
410: * true. If not, return false
411: *
412: * @param request the servlet request object
413: * @param forceDB the data context to login to
414: * @return true if successfull
415: * @throws Exception upon error
416: */
417: public boolean loginViaCookie(HttpServletRequest request,
418: String forceDB) throws Exception {
419:
420: /* otherwise, try the cookie */
421: String userName = null;
422: String password = null;
423: String db = ("");
424: Cookie[] cookies = request.getCookies();
425:
426: if (cookies == null) {
427: if (log.isDebugEnabled()) {
428: log.debug("No cookies present");
429: }
430:
431: return false;
432: }
433: for (int i = 0; i < cookies.length; i++) {
434: String name = StringUtil.notNull(cookies[i].getName());
435: String value = StringUtil.notNull(cookies[i].getValue());
436:
437: if (name.equalsIgnoreCase("UserName")) {
438: userName = CookieUtil.cookieDecode(value);
439: } else if (name.equalsIgnoreCase("Password")) {
440: password = CookieUtil.cookieDecode(value);
441: } else if (name.equalsIgnoreCase("db")) {
442: db = CookieUtil.cookieDecode(value);
443: }
444: } /* for */
445:
446: if (forceDB != null) {
447: db = forceDB;
448: }
449:
450: /* Check if this is a valid config key */
451: String oneKey = null;
452: boolean keyOk = false;
453:
454: if (db != null && db.length() > 0) {
455: for (Enumeration eck = ConfigManager.getAllConfigKeys(); eck
456: .hasMoreElements();) {
457: oneKey = (String) eck.nextElement();
458:
459: if (oneKey.equals(db)) {
460: keyOk = true;
461: break;
462: }
463: }
464: if (!keyOk) {
465: log.warn("'db' in cookie with value '" + db
466: + "' was not a valid config key.");
467:
468: return false;
469: }
470: if (log.isDebugEnabled()) {
471: log.debug("db cookie '" + db + "'");
472: }
473: }
474:
475: if (((userName == null) || (password == null) || (db == null))
476: || ((userName.length() == 0) || (db.length() == 0))) {
477: if (log.isDebugEnabled()) {
478: log.debug("Didn't get all 3 cookies");
479: }
480:
481: return false;
482: }
483:
484: if (userName.equalsIgnoreCase("NONE")) {
485: logInAsNone(request, db);
486: }
487:
488: User myUser = new User();
489: myUser.setDataContext(db);
490: myUser.setLoginName(userName);
491:
492: if (!myUser.find()) {
493: if (log.isDebugEnabled()) {
494: log.debug("Cookie username '" + userName
495: + "' not found in db '" + db
496: + "'. User logged in as 'NONE'");
497: }
498:
499: return false;
500: } /* if user not found */
501:
502: if (!myUser.getAccountStatus().equals(
503: User.ACTIVE_ACCOUNT_STATUS)) {
504: log
505: .warn("Attempted login to an inactive account. Client i.p. "
506: + request.getRemoteAddr()
507: + " Account status: "
508: + myUser.getAccountStatus()
509: + " Login Name: "
510: + userName
511: + " passwd: "
512: + password + " DB" + db);
513: return false; // no need to throw exception here; just do not give auth
514: }
515:
516: if (!myUser.passwordEquals(StringUtil.notNull(password))) {
517: if (log.isDebugEnabled()) {
518: log
519: .debug("Cookie password didn't match, User logged in as 'NONE'");
520: }
521:
522: return false;
523: }
524:
525: if (log.isInfoEnabled()) {
526: log.info("User " + myUser.getDisplayName() + " ("
527: + userName + ") logged in via cookie as '"
528: + userName + "'");
529: }
530:
531: doSuccessfulAuth(request, userName, db, myUser);
532: return true;
533: }
534:
535: private void doSuccessfulAuth(HttpServletRequest request,
536: String userName, String db, User myUser)
537: throws ServletException, DBException, LogException {
538:
539: GenericSession.removeAttribute(request, CurrentLogin.LOGIN_KEY);
540:
541: // Remove the existing login record, if there is one for this session already
542: ConfigManager.removeSession(GenericSession.getId(request));
543:
544: CurrentLogin myLogin = CurrentLogin.newInstance(userName,
545: request.getRemoteAddr(), db, myUser.getUid());
546: GenericSession.setAttribute(request, CurrentLogin.LOGIN_KEY,
547: myLogin);
548:
549: //The following line sets the particular instance of requestRegistry
550: //into the threadlocal context.
551: new MutableRequestRegistry(db, myUser);
552:
553: try {
554: myUser.postLogin();
555: Messages.establishLocale(request);
556: } catch (DBException de) {
557: log
558: .warn(
559: "Post-login processing did not complete successfully",
560: de);
561: }
562: }
563: } /* CheckLogin */
|