0001: /* ====================================================================
0002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
0003: *
0004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * 2. Redistributions in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * 3. The end-user documentation included with the redistribution,
0019: * if any, must include the following acknowledgment:
0020: * "This product includes software developed by Jcorporate Ltd.
0021: * (http://www.jcorporate.com/)."
0022: * Alternately, this acknowledgment may appear in the software itself,
0023: * if and wherever such third-party acknowledgments normally appear.
0024: *
0025: * 4. "Jcorporate" and product names such as "Expresso" must
0026: * not be used to endorse or promote products derived from this
0027: * software without prior written permission. For written permission,
0028: * please contact info@jcorporate.com.
0029: *
0030: * 5. Products derived from this software may not be called "Expresso",
0031: * or other Jcorporate product names; nor may "Expresso" or other
0032: * Jcorporate product names appear in their name, without prior
0033: * written permission of Jcorporate Ltd.
0034: *
0035: * 6. No product derived from this software may compete in the same
0036: * market space, i.e. framework, without prior written permission
0037: * of Jcorporate Ltd. For written permission, please contact
0038: * partners@jcorporate.com.
0039: *
0040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
0044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
0046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0051: * SUCH DAMAGE.
0052: * ====================================================================
0053: *
0054: * This software consists of voluntary contributions made by many
0055: * individuals on behalf of the Jcorporate Ltd. Contributions back
0056: * to the project(s) are encouraged when you make modifications.
0057: * Please send them to support@jcorporate.com. For more information
0058: * on Jcorporate Ltd. and its products, please see
0059: * <http://www.jcorporate.com/>.
0060: *
0061: * Portions of this software are based upon other open source
0062: * products and are subject to their respective licenses.
0063: */
0064:
0065: package com.jcorporate.expresso.services.controller;
0066:
0067: import com.jcorporate.expresso.core.ExpressoConstants;
0068: import com.jcorporate.expresso.core.controller.Controller;
0069: import com.jcorporate.expresso.core.controller.ControllerException;
0070: import com.jcorporate.expresso.core.controller.ControllerRequest;
0071: import com.jcorporate.expresso.core.controller.ControllerResponse;
0072: import com.jcorporate.expresso.core.controller.ErrorCollection;
0073: import com.jcorporate.expresso.core.controller.Input;
0074: import com.jcorporate.expresso.core.controller.NonHandleableException;
0075: import com.jcorporate.expresso.core.controller.Output;
0076: import com.jcorporate.expresso.core.controller.ServletControllerRequest;
0077: import com.jcorporate.expresso.core.controller.State;
0078: import com.jcorporate.expresso.core.controller.Transition;
0079: import com.jcorporate.expresso.core.controller.session.PersistentSession;
0080: import com.jcorporate.expresso.core.dataobjects.Securable;
0081: import com.jcorporate.expresso.core.db.DBException;
0082: import com.jcorporate.expresso.core.dbobj.DBObject;
0083: import com.jcorporate.expresso.core.dbobj.ValidValue;
0084: import com.jcorporate.expresso.core.i18n.Messages;
0085: import com.jcorporate.expresso.core.misc.ConfigManager;
0086: import com.jcorporate.expresso.core.misc.ConfigurationException;
0087: import com.jcorporate.expresso.core.misc.SerializableString;
0088: import com.jcorporate.expresso.core.misc.StringUtil;
0089: import com.jcorporate.expresso.core.security.User;
0090: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0091: import com.jcorporate.expresso.services.dbobj.RegistrationDomain;
0092: import com.jcorporate.expresso.services.dbobj.Setup;
0093: import com.jcorporate.expresso.services.validation.AuthValidationException;
0094: import com.jcorporate.expresso.services.validation.ValidationEntry;
0095: import org.apache.log4j.Logger;
0096: import org.apache.struts.Globals;
0097: import org.apache.struts.action.ActionForward;
0098: import org.apache.struts.config.ForwardConfig;
0099:
0100: import javax.servlet.ServletException;
0101: import javax.servlet.http.HttpServletRequest;
0102: import javax.servlet.http.HttpServletResponse;
0103: import java.util.Enumeration;
0104: import java.util.Vector;
0105:
0106: /**
0107: * Main Login Controller - used for login/logout and basic interaction with
0108: * the registration system. This class recognizes the 'registration' classHandler
0109: * name in the expresso-config.xml It uses the classname in that field to
0110: * construct and forward to the appropriate registration class.
0111: */
0112: public class SimpleLoginController extends LoginController {
0113:
0114: private static Logger log = Logger
0115: .getLogger(SimpleLoginController.class);
0116:
0117: /**
0118: * LoginController constructor. Sets all the states and parameters
0119: * for the system.
0120: */
0121: public SimpleLoginController() {
0122: super ();
0123: State promptLogin = new State("promptLogin", "Prompt Login");
0124: promptLogin.addOptionalParameter("next"); //Where do we link after login?
0125: promptLogin.addOptionalParameter("immediate"); //Do we even display the
0126: //confirmation page or do we
0127: //just jump straight to the
0128: //page on success? Y or N
0129: promptLogin.setSecure(true);
0130: promptLogin.addOptionalParameter("next"); //Where do we link after login?
0131: promptLogin.addOptionalParameter("immediate"); //Do we even display the
0132: //confirmation page or do we
0133: //just jump straight to the
0134: //page on success?
0135: promptLogin.addOptionalParameter("LoginName");
0136: addState(promptLogin);
0137:
0138: State processLogin = new State("processLogin", "Login");
0139: processLogin.setSecure(true);
0140: addState(processLogin);
0141:
0142: State processLogout = new State("processLogout", "Logout");
0143: addState(processLogout);
0144:
0145: State promptChangePassword = new State("promptChangePassword",
0146: "Prompt Change Password");
0147: promptChangePassword.setSecure(true);
0148: addState(promptChangePassword);
0149:
0150: State processChangePassword = new State(
0151: "processChangePassword", "Change Password");
0152: processChangePassword.setSecure(true);
0153: addState(processChangePassword);
0154:
0155: State emailValidate = new State("emailValidate",
0156: "Validate User's Email Address");
0157: emailValidate.addRequiredParameter("db");
0158: emailValidate.addRequiredParameter("UserName");
0159: emailValidate.addOptionalParameter("loginController");
0160: addState(emailValidate);
0161:
0162: State promptSendPassword = new State("promptSendPassword",
0163: "Prompt Send Password");
0164: addState(promptSendPassword);
0165:
0166: State processSendPassword = new State("processSendPassword",
0167: "Reset & Send Password");
0168: processSendPassword.addParameter("Email", false,
0169: DBObject.EMAIL_MASK,
0170: "You must enter a valid email address");
0171: addState(processSendPassword);
0172:
0173: State s = new State("promptRevalidate", "Prompt Revalidate");
0174: this .addState(s);
0175:
0176: s = new State("processRevalidate", "Process Revalidate");
0177: s.addParameter("Email", false, DBObject.EMAIL_MASK,
0178: "You must enter a valid email address");
0179: this .addState(s);
0180:
0181: setInitialState("promptLogin");
0182: this
0183: .setSchema(com.jcorporate.expresso.core.ExpressoSchema.class);
0184: }
0185:
0186: /**
0187: * If the user has validated the email sent to validate change password request,
0188: * then this method actually resets thepassword and sends notification.
0189: *
0190: * @param request The framework controller request
0191: * @param response The framework ControllerResponse object
0192: * @throws ControllerException upon logic error
0193: * @throws NonHandleableException upon fatal error
0194: */
0195: protected void runEmailValidateState(ControllerRequest request,
0196: ControllerResponse response) throws ControllerException,
0197: NonHandleableException {
0198: // The db context for the user (Note: this is different from the Validation entry context, which
0199: // could very well be in a different DB context)
0200: String dbname = StringUtil.notNull(request.getParameter("db"));
0201:
0202: // The login name of the user
0203: String loginName = StringUtil.notNull(request
0204: .getParameter("UserName"));
0205: String registrationController = StringUtil.notNull(request
0206: .getParameter("RegistrationController"));
0207: if (registrationController.length() == 0) {
0208: registrationController = this
0209: .getDefaultRegistrationController().getClass()
0210: .getName();
0211: }
0212:
0213: String loginController = StringUtil.notNull(request
0214: .getParameter("LoginController"));
0215: try {
0216: ErrorCollection errors = new ErrorCollection();
0217:
0218: // Make sure that the user with this loginName actually exists
0219: User user = new User();
0220: user.setDataContext(dbname);
0221: user.setLoginName(loginName);
0222:
0223: if (!user.find()) {
0224: //errors.addError("Account \"" + loginName + "\" not found");
0225: errors.addError("login.accountnotfound",
0226: (Object) loginName);
0227: }
0228: // Make sure the User record has not been disabled for some reason
0229: if (errors.isEmpty()) {
0230: if (user.getAccountStatus().equals("D")) {
0231: errors.addError("error.login.accountdisabled",
0232: (Object) loginName);
0233: }
0234: }
0235:
0236: if (!errors.isEmpty()) {
0237: response.saveErrors(errors);
0238: return;
0239: }
0240:
0241: // Reset the user's password and send email
0242: String password = null;
0243: if (errors.isEmpty()) {
0244: password = user.randomPassword();
0245: user.setPassword(password);
0246: user.setAccountStatus("A");
0247: user.update();
0248:
0249: FastStringBuffer msg = FastStringBuffer.getInstance();
0250: msg.append(response.getString("passwdReset", loginName,
0251: password, Setup.getValue(request
0252: .getDataContext(), "CompanyName"),
0253: Setup.getValue(request.getDataContext(),
0254: "HomePageURL")));
0255: user.notify(response.getString("passwdResetSubject"),
0256: msg.toString());
0257: msg.release();
0258: }
0259: // If no errors happened so far, just create an Output to state
0260: // the success and what happened
0261: if (errors.isEmpty()) {
0262: Output o = new Output("successMessage",
0263: "Password succesfully reset and email sent to user \""
0264: + loginName + "\" ");
0265: response.add(o);
0266: FastStringBuffer msg = FastStringBuffer.getInstance();
0267: try {
0268: msg
0269: .append("You may log into the system using the password: \n");
0270: msg.append(password);
0271: msg
0272: .append("\n This password has been emailed to you ");
0273: msg.append("and should be kept in a safe place");
0274:
0275: response.add(new Output("passwordMessage", msg
0276: .toString()));
0277: } finally {
0278: msg.release();
0279: }
0280:
0281: Transition login = new Transition();
0282: login.setLabel("Log In");
0283: login.setName("promptLogin");
0284: login.addParam(Controller.CONTROLLER_PARAM_KEY,
0285: loginController);
0286: login.addParam("dbContext", dbname);
0287: response.add(login);
0288:
0289: Transition register = new Transition();
0290: register.setLabel("Register");
0291: register.setName("promptSelfRegister");
0292: register.addParam(Controller.CONTROLLER_PARAM_KEY,
0293: registrationController);
0294: register.addParam("dbContext", dbname);
0295: response.add(register);
0296: } else {
0297:
0298: // Errors happened, add the error collection to the response
0299: response.saveErrors(errors);
0300: }
0301: } catch (DBException dbe) {
0302: throw new ControllerException("DB error", dbe);
0303: }
0304:
0305: }
0306:
0307: /**
0308: * @return the title of this controller
0309: */
0310: public String getTitle() {
0311: return ("Expresso Login");
0312: }
0313:
0314: /**
0315: * Processes the "change my password" request.
0316: *
0317: * @param request The framework controller request
0318: * @param response The framework ControllerResponse object
0319: * @throws ControllerException upon logic error
0320: * @throws NonHandleableException upon fatal error
0321: */
0322: protected void runProcessChangePasswordState(
0323: ControllerRequest request, ControllerResponse response)
0324: throws ControllerException, NonHandleableException {
0325: ErrorCollection errors = new ErrorCollection();
0326: String loginName = request.getUser();
0327:
0328: try {
0329: User myUser = new User();
0330: myUser.setDataContext(request.getDataContext());
0331: myUser.setLoginName(loginName);
0332:
0333: if (loginName.equals("")
0334: || loginName.equals(User.UNKNOWN_USER)
0335: || !myUser.find()) {
0336: delayLogin(); //Slow 'em down
0337: errors.addError(response
0338: .getString("error.login.mustloginchngpaswd"));
0339: } else if (!myUser.passwordEquals(StringUtil
0340: .notNull(request.getParameter("oldPassword")))) {
0341: errors.addError(response.getString(
0342: "error.login.passwordinvalid", loginName));
0343: }
0344: if (errors.getErrorCount() < 1) {
0345: if (!myUser.getAccountStatus().equals("A")) {
0346: log
0347: .warn("User \""
0348: + loginName
0349: + "\" attempted changin password, denied because account status is \""
0350: + myUser.getAccountStatus() + "\"");
0351: delayLogin(); //Slow 'em down
0352: errors.addError(response.getString(
0353: "error.login.disablednochngpaswd",
0354: loginName));
0355: }
0356: }
0357: if (errors.getErrorCount() < 1) {
0358: if (!request.getParameter("Password").equals(
0359: request.getParameter("password_verify"))) {
0360:
0361: //password and verify do not match. Add an error.
0362: errors.addError("error.login.passwdnomatch");
0363: }
0364: }
0365: if (errors.getErrorCount() < 1) {
0366: User user = new User();
0367: user.setDataContext(request.getDataContext());
0368: user.setUid(request.getUid());
0369: user.retrieve();
0370: user.setPassword(request.getParameter("Password"));
0371: user.update();
0372: }
0373: } catch (DBException dbe) {
0374: throw new ControllerException(dbe);
0375: } finally {
0376: if (errors.getErrorCount() < 1) {
0377: response.clearFormCache();
0378:
0379: Output successMessage = new Output("successMessage",
0380: response.getString("PasswordChangeSuccess",
0381: loginName));
0382: response.addOutput(successMessage);
0383: response.addTransition(new Transition(
0384: "promptChangePassword", this ));
0385: response.addTransition(new Transition("promptLogin",
0386: this ));
0387: response.addTransition(new Transition("processLogout",
0388: this ));
0389:
0390: Transition editPref = new Transition();
0391: editPref.setName("editPreferences");
0392: editPref
0393: .addParam(Controller.CONTROLLER_PARAM_KEY,
0394: "com.jcorporate.expresso.services.controller.EditUserPreference");
0395: editPref.addParam(STATE_PARAM_KEY, "edit");
0396: response.add(editPref);
0397:
0398: Transition showDBMenu = new Transition();
0399: showDBMenu.setName("showDBMenu");
0400: showDBMenu.addParam(Controller.CONTROLLER_PARAM_KEY,
0401: this .getDefaultRegistrationController()
0402: .getClass().getName());
0403: showDBMenu.addParam(STATE_PARAM_KEY, "showDBMenu");
0404: response.add(showDBMenu);
0405: } else {
0406: response.saveErrors(errors);
0407: response.setFormCache();
0408: transition("promptChangePassword", request, response);
0409: }
0410: }
0411: }
0412:
0413: /**
0414: * Processes the login request.... loops back to the promptLoginState
0415: * if there's an error processing this system.
0416: *
0417: * @param request The framework controller request
0418: * @param response The framework ControllerResponse object
0419: * @throws ControllerException upon logic error
0420: * @throws NonHandleableException upon fatal error
0421: */
0422: protected void runProcessLoginState(ControllerRequest request,
0423: ControllerResponse response) throws ControllerException,
0424: NonHandleableException {
0425: ErrorCollection errors = new ErrorCollection();
0426: PersistentSession session = request.getSession();
0427: String destination = null;
0428:
0429: /* Normally, accessing the HttpServletRequest/Response objects is a very bad
0430: * idea. The fact that this controller does it means that it is non-portable
0431: * outside the servlet environment
0432: */
0433: ServletControllerRequest sr = (ServletControllerRequest) request;
0434: HttpServletResponse hres = (HttpServletResponse) sr
0435: .getServletResponse();
0436: HttpServletRequest hreq = (HttpServletRequest) sr
0437: .getServletRequest();
0438: String dbContext = StringUtil.notNull(request
0439: .getParameter("dbContext"));
0440: request.setDataContext(dbContext);
0441:
0442: //
0443: //Remove wildcards so that it isn't used in finding users.
0444: //
0445: String loginName = StringUtil.replaceAll(StringUtil
0446: .notNull(request.getParameter("LoginName")), "%", "");
0447: int uid = 0;
0448:
0449: try {
0450: uid = attemptLogin(request, response, errors, hreq, hres,
0451: session);
0452: } catch (DBException dbe) {
0453: throw new ControllerException(dbe);
0454: } finally {
0455: if (errors.getErrorCount() > 0) {
0456: log.debug("Transitioning back to login state...");
0457: response.saveErrors(errors);
0458: response.setFormCache();
0459: transition("promptLogin", request, response);
0460:
0461: return;
0462: }
0463:
0464: ///////////////////////////////////////
0465: // good login
0466: ///////////////////////////////////////
0467: request.setUser(loginName);
0468: if (log.isDebugEnabled()) {
0469: log.debug("good login for uid: " + uid);
0470: }
0471:
0472: // set locale
0473: try {
0474: request.getSession().removePersistentAttribute(
0475: Messages.LOCALE_KEY);
0476: request.getSession().removePersistentAttribute(
0477: Globals.ERROR_KEY);
0478: Messages.establishLocale(hreq);
0479: } catch (ServletException se) {
0480: log.error("Cannot manipulate local in session", se);
0481:
0482: // do not abort entire action since this error is recoverable
0483: }
0484:
0485: /* Template method, override in your derived class to do any
0486: work you may want to accomplish after a successful login */
0487: postLoginProcessing(request, response);
0488: String origURL = null;
0489: if (session != null) {
0490: SerializableString originalURL = (SerializableString) session
0491: .getPersistentAttribute(ExpressoConstants.CONTROLLER_ORIGINAL_URL_KEY);
0492:
0493: if (originalURL != null) {
0494: origURL = originalURL.toString();
0495: session
0496: .removePersistentAttribute(ExpressoConstants.CONTROLLER_ORIGINAL_URL_KEY);
0497: }
0498: }
0499: String nextURL = StringUtil.notNull(request
0500: .getParameter("next"));
0501: if (origURL != null && origURL.length() > 0) {
0502:
0503: /** redirect to original destination
0504: * BUT be careful to use an HTTP tag redirection
0505: * so that the login cookie can be planted. we cannot
0506: * simply call sendRedirect because then the cookie
0507: * from the current response (set during attemptLogin)
0508: * would not reach the browser. instead, we send a result
0509: * page, which plants the cookie, and that page has a
0510: * redirect tag.
0511: */
0512:
0513: // provide redirect page with a link to destination in case the browser does
0514: // not redirect properly
0515: hreq.setAttribute("destination", origURL);
0516: } else if (nextURL.length() > 0) {
0517: hreq.setAttribute("destination", nextURL);
0518: } else {
0519:
0520: /**
0521: *
0522: if we got here, someone went directly to login page, so there
0523: was no original "destination" besides the login page.
0524: in that case, send them to a some "home" page,
0525: specified as the forward "home" forward
0526: */
0527:
0528: // use the previous status.jsp page if we do not find
0529: // a "home" global forward specification
0530: destination = null;
0531:
0532: ActionForward fwd = null;
0533: try {
0534: ServletControllerRequest req = (ServletControllerRequest) request;
0535: ForwardConfig fc = req.getMapping()
0536: .getModuleConfig()
0537: .findForwardConfig("home");
0538: fwd = new ActionForward(fc.getName(), fc.getPath(),
0539: fc.getRedirect(), fc.getContextRelative());
0540: } catch (Exception e) {
0541: log
0542: .error(
0543: "cannot cast to get servlet request: ",
0544: e);
0545: }
0546:
0547: if (fwd == null) {
0548: log
0549: .warn("global forward 'home' is missing using status.jsp");
0550: response.addTransition(new Transition(
0551: "promptChangePassword", this ));
0552: response.addTransition(new Transition(
0553: "promptLogin", this ));
0554: response.addTransition(new Transition(
0555: "processLogout", this ));
0556:
0557: Transition editPref = new Transition();
0558: editPref.setName("editPreferences");
0559: editPref
0560: .addParam(
0561: Controller.CONTROLLER_PARAM_KEY,
0562: com.jcorporate.expresso.services.controller.EditUserPreference.class
0563: .getName());
0564: editPref.addParam(STATE_PARAM_KEY, "edit");
0565: response.add(editPref);
0566:
0567: Transition showDBMenu = new Transition();
0568: showDBMenu.setName("showDBMenu");
0569: showDBMenu.addParam(
0570: Controller.CONTROLLER_PARAM_KEY, this
0571: .getDefaultRegistrationController()
0572: .getClass().getName());
0573: showDBMenu.addParam(STATE_PARAM_KEY, "showDBMenu");
0574: response.add(showDBMenu);
0575:
0576: } else {
0577: destination = ConfigManager.getContextPath()
0578: + fwd.getPath();
0579:
0580: hreq.setAttribute("destination", destination);
0581: if (log.isInfoEnabled()) {
0582: log.info("via redirect.jsp, redirecting to: "
0583: + destination);
0584: }
0585: }
0586: }
0587: }
0588:
0589: // redirect.jsp actually handles redirect
0590: }
0591:
0592: /**
0593: * Logs a user out of the system and invalidates their session.
0594: *
0595: * @param request The framework controller request
0596: * @param response The framework ControllerResponse object
0597: * @throws ControllerException upon logic error
0598: */
0599: protected void runProcessLogoutState(ControllerRequest request,
0600: ControllerResponse response) throws ControllerException {
0601: PersistentSession session = request.getSession();
0602: String loginName = request.getUser();
0603: String successMessage = loginName + " has been logged out";
0604:
0605: if (loginName.equals("")) {
0606: successMessage = "You were not logged in anyway!";
0607: }
0608:
0609: if (request instanceof ServletControllerRequest) {
0610: ServletControllerRequest sreq = (ServletControllerRequest) request;
0611: LoginController.setCookie(null, null,
0612: (HttpServletResponse) sreq.getServletResponse(),
0613: true, request.getDataContext());
0614:
0615: }
0616: Output successOutput = new Output();
0617: successOutput.setName("sucessMessage");
0618: successOutput.setContent(successMessage);
0619: response.add(successOutput);
0620: response.setUser(User.UNKNOWN_USER);
0621:
0622: Transition pr = new Transition("promptSelfRegister", this
0623: .getDefaultRegistrationController());
0624: pr.addParam("dbContext", request.getDataContext());
0625: response.add(pr);
0626:
0627: Transition pl = new Transition("promptLogin", this );
0628: pl.addParam("dbContext", request.getDataContext());
0629: response.add(pl);
0630:
0631: session.setClientAttribute("UserName", "NONE");
0632: session.setClientAttribute("Password", "NONE");
0633: session.setClientAttribute("dbContext", "NONE");
0634: session.removePersistentAttribute("CurrentLogin");
0635: session.invalidate();
0636:
0637: postLogoutProcessing(request, response);
0638:
0639: }
0640:
0641: /**
0642: * Template Method pattern. override in subclasses as necessary
0643: *
0644: * @param request The framework controller request
0645: * @param response The framework ControllerResponse object
0646: */
0647: public void postLogoutProcessing(ControllerRequest request,
0648: ControllerResponse response) throws ControllerException {
0649: // * Template Method pattern. override in subclasses as necessary
0650: }
0651:
0652: /**
0653: * Process the "Please Send me a new password" state.
0654: *
0655: * @param request The framework controller request
0656: * @param response The framework ControllerResponse object
0657: * @throws ControllerException upon logic error
0658: * @throws NonHandleableException upon fatal error
0659: */
0660: protected void runProcessSendPasswordState(
0661: ControllerRequest request, ControllerResponse response)
0662: throws ControllerException, NonHandleableException {
0663: //
0664: //Validate input
0665: //
0666: ErrorCollection errors = request.getErrorCollection();
0667: if (errors == null) {
0668: errors = new ErrorCollection();
0669: }
0670:
0671: String dbContext = StringUtil.notNull(request
0672: .getParameter("dbContext"));
0673:
0674: if (!dbContext.equals("")) {
0675: request.setDataContext(dbContext);
0676: }
0677:
0678: response.add(new Transition("promptSelfRegister", this
0679: .getDefaultRegistrationController()));
0680: response.add(new Transition("promptLogin", this ));
0681: String email = StringUtil.replaceAll(StringUtil.notNull(request
0682: .getParameter("Email")), "%", "");
0683:
0684: if (email == null || email.length() == 0) {
0685: errors.addError("error.login.invalidemail");
0686: }
0687:
0688: if (errors.getErrorCount() > 0) {
0689: response.saveErrors(errors);
0690: transition("promptSendPassword", request, response);
0691: return;
0692: }
0693:
0694: try {
0695: User myUser = new User();
0696: myUser.setDataContext(request.getDataContext());
0697: myUser.setEmail(email);
0698:
0699: if (!myUser.find()) {
0700: errors.addError("error.login.nouseremailfound");
0701: }
0702:
0703: String loginName = myUser.getLoginName();
0704:
0705: if (errors.getErrorCount() < 1) {
0706: if (!myUser.getAccountStatus().equals("A")) {
0707: log
0708: .warn("User \""
0709: + loginName
0710: + "\" attempted password reset, denied because account status is \""
0711: + myUser.getAccountStatus() + "\"");
0712: errors
0713: .addError("error.login.accountdiablednosendpaswd");
0714: }
0715: }
0716: if (errors.getErrorCount() < 1) {
0717: boolean emailValidate = false;
0718: RegistrationDomain rd = null;
0719: rd = new RegistrationDomain();
0720: rd.setDataContext(request.getDataContext());
0721: rd.setField("Name", myUser.getRegistrationDomain());
0722:
0723: if (!rd.find()) {
0724: throw new ControllerException(
0725: "Registration domain \""
0726: + myUser.getRegistrationDomain()
0727: + "\" has not been defined");
0728: }
0729: if (rd.getField("EmailValidate").equals("Y")) {
0730: emailValidate = true;
0731: }
0732:
0733: /* HACK: Even though registration doesn't require */
0734: /* email validate, passwords *always* do */
0735: emailValidate = true;
0736:
0737: ServletControllerRequest sparams = (ServletControllerRequest) request;
0738: HttpServletRequest hreq = (HttpServletRequest) sparams
0739: .getServletRequest();
0740:
0741: if (emailValidate) {
0742: String emailAuthCode = myUser.getEmailAuthCode();
0743: myUser.setEmailValCode(emailAuthCode);
0744: myUser.update();
0745:
0746: try {
0747: ValidationEntry ve = new ValidationEntry(
0748: request.getDataContext());
0749: ve
0750: .setValidationHandler("com.jcorporate.expresso.services.validation.ChangePasswordValidator");
0751: ve.setTitle("Change Password Validation");
0752: ve.setDesc("user=" + loginName + ", db="
0753: + request.getDataContext());
0754: ve.setServer(hreq.getServerName());
0755: ve.setPort(Integer.toString(hreq
0756: .getServerPort()));
0757: ve.setContextPath(hreq.getContextPath());
0758: ve.addParam("db", request.getDataContext());
0759: ve.addParam("UserName", loginName);
0760: ve.addParam("LoginController", this .getClass()
0761: .getName());
0762: ve.submit();
0763: } catch (AuthValidationException avex) {
0764: throw new ControllerException(
0765: "Validation framework problem", avex);
0766: }
0767:
0768: Output successMessage = new Output(
0769: "successMessage",
0770: "Email was sent to \""
0771: + email
0772: + "\" to verify password change request");
0773: response.addOutput(successMessage);
0774: } else {
0775: String password = myUser.randomPassword();
0776: myUser.setPassword(password);
0777: myUser.update();
0778:
0779: FastStringBuffer msg = FastStringBuffer
0780: .getInstance();
0781: msg.append(response.getString("passwdReset", myUser
0782: .getLoginName(), password, Setup.getValue(
0783: request.getDataContext(), "CompanyName"),
0784: Setup.getValue(request.getDataContext(),
0785: "HomePageURL")));
0786: myUser.notify(response
0787: .getString("passwdResetSubject"), msg
0788: .toString());
0789: msg.release();
0790: Output successMessage = new Output(
0791: "successMessage", "Password for \"" + email
0792: + "\" was reset and an email sent");
0793: response.addOutput(successMessage);
0794: }
0795: }
0796: } catch (Exception dbe) {
0797: throw new ControllerException(dbe);
0798: } finally {
0799: if (errors.getErrorCount() < 1) {
0800: response.clearFormCache();
0801: } else {
0802: response.saveErrors(errors);
0803: response.setFormCache();
0804: transition("promptSendPassword", request, response);
0805: return;
0806: }
0807: }
0808: }
0809:
0810: /**
0811: * Displays the 'change password' page.
0812: *
0813: * @param request The framework controller request
0814: * @param response The framework ControllerResponse object
0815: * @throws ControllerException upon logic error
0816: */
0817: protected void runPromptChangePasswordState(
0818: ControllerRequest request, ControllerResponse response)
0819: throws ControllerException {
0820: response.clearFormCache();
0821:
0822: Input oldPassword = new Input();
0823: oldPassword.setName("oldPassword");
0824: oldPassword.setLabel(response.getString("CurrentPassword"));
0825:
0826: String opw = StringUtil.notNull(response
0827: .getFormCache("oldPassword"));
0828: oldPassword.setDefaultValue(opw);
0829: oldPassword.setDisplayLength(15);
0830: oldPassword.setMaxLength(30);
0831: oldPassword.setType("password");
0832: response.addInput(oldPassword);
0833:
0834: Input password = new Input();
0835: password.setName("Password");
0836: password.setLabel(response.getString("NewPassword"));
0837:
0838: String pw = StringUtil.notNull(response
0839: .getFormCache("Password"));
0840: password.setDefaultValue(pw);
0841: password.setDisplayLength(15);
0842: password.setMaxLength(30);
0843: password.setType("password");
0844: response.addInput(password);
0845:
0846: Input password_verify = new Input();
0847: password_verify.setName("password_verify");
0848: password_verify.setLabel(response.getString("RetypePassword"));
0849:
0850: String pwv = StringUtil.notNull(response
0851: .getFormCache("password_verify"));
0852: password_verify.setDefaultValue(pwv);
0853: password_verify.setDisplayLength(15);
0854: password_verify.setMaxLength(30);
0855: password_verify.setType("password");
0856: response.addInput(password_verify);
0857:
0858: //Transition change = new Transition(response.getString("changePasswordTitle"), this);
0859: Transition change = new Transition("processChangePassword",
0860: this );
0861: change.setLabel(response.getString("changePasswordTitle"));
0862: response.add(change);
0863: response.addTransition(new Transition("promptChangePassword",
0864: this ));
0865: response.addTransition(new Transition("promptLogin", this ));
0866: response.addTransition(new Transition("processLogout", this ));
0867:
0868: Transition editPref = new Transition();
0869: editPref.setName("editPreferences");
0870: editPref
0871: .addParam(Controller.CONTROLLER_PARAM_KEY,
0872: "com.jcorporate.expresso.services.controller.EditUserPreference");
0873: editPref.addParam(STATE_PARAM_KEY, "edit");
0874: response.add(editPref);
0875:
0876: Transition showDBMenu = new Transition();
0877: showDBMenu.setName("showDBMenu");
0878: showDBMenu.addParam(Controller.CONTROLLER_PARAM_KEY, this
0879: .getDefaultRegistrationController().getClass()
0880: .getName());
0881: showDBMenu.addParam(STATE_PARAM_KEY, "showDBMenu");
0882: response.add(showDBMenu);
0883: }
0884:
0885: /**
0886: * Prompts the user for login.
0887: *
0888: * @param request The ControllerRequest object
0889: * @param response The ControllerResponse object
0890: */
0891: protected void runPromptLoginState(ControllerRequest request,
0892: ControllerResponse response) throws ControllerException {
0893: PersistentSession session = request.getSession();
0894: Input dbContext = new Input("dbContext");
0895: String useDB = StringUtil.notNull(request
0896: .getParameter("dbContext"));
0897:
0898: if (!useDB.equals("")) {
0899: request.setDataContext(useDB);
0900: response.addOutput(new Output("dbContext", useDB));
0901: } else {
0902: dbContext.setDefaultValue(request.getDataContext());
0903: dbContext.setLabel(response.getString("Context/Database_"));
0904:
0905: String oneConfigKey = null;
0906: String oneDescrip = null;
0907: Vector v = new Vector();
0908:
0909: for (Enumeration ie = ConfigManager.getAllConfigKeys(); ie
0910: .hasMoreElements();) {
0911: oneConfigKey = (String) ie.nextElement();
0912: oneDescrip = "";
0913:
0914: try {
0915: oneDescrip = StringUtil.notNull(ConfigManager
0916: .getContext(oneConfigKey).getDescription());
0917:
0918: if (oneDescrip.equals("")) {
0919: oneDescrip = oneConfigKey;
0920: }
0921: /* If it's not an expresso context, you can't log in to it */
0922: if (ConfigManager.getContext(oneConfigKey)
0923: .hasSetupTables()) {
0924: v.addElement(new ValidValue(oneConfigKey,
0925: oneDescrip));
0926: }
0927: } catch (ConfigurationException ce) {
0928: throw new ControllerException(ce);
0929: }
0930: }
0931:
0932: dbContext.setValidValues(v);
0933: response.addInput(dbContext);
0934: }
0935:
0936: // Fill in the Login field with values in following priority: form-cache, cookie
0937: Input loginName = new Input();
0938: loginName.setName("LoginName");
0939:
0940: String ln = StringUtil.notNull(response
0941: .getFormCache("LoginName"));
0942:
0943: if (ln.equals("")) {
0944: ln = request.getUser();
0945: }
0946: if (ln.equals(User.UNKNOWN_USER)) {
0947: ln = "";
0948: }
0949:
0950: loginName.setDefaultValue(ln);
0951: loginName.setDisplayLength(15);
0952: loginName.setMaxLength(30);
0953: loginName.setLabel("Login");
0954: response.addInput(loginName);
0955:
0956: // Fill in the Password field with values in following priority: form-cache, cookie
0957: Input password = new Input();
0958: password.setName("Password");
0959: password.setLabel("Password");
0960:
0961: String pw = StringUtil.notNull(response
0962: .getFormCache("Password"));
0963:
0964: if ("".equals(pw)) {
0965: pw = StringUtil.notNull(session
0966: .getClientAttribute("Password"));
0967: }
0968:
0969: if ("NONE".equals(pw)) {
0970: pw = "";
0971: }
0972:
0973: password.setDefaultValue(pw);
0974: password.setDisplayLength(15);
0975: password.setMaxLength(30);
0976: password.setType("password");
0977: response.addInput(password);
0978:
0979: // Fill in the Remember field
0980: Input remember = new Input("Remember");
0981: remember.setLabel("Remember Login");
0982:
0983: String rm = response.getFormCache("Remember");
0984:
0985: if (rm == null || rm.length() == 0) {
0986: rm = "Y";
0987: }
0988:
0989: remember.setType("checkbox");
0990: remember.setDefaultValue(rm);
0991: response.addInput(remember);
0992:
0993: //
0994: //Put a hidden element on the form to pass the next parameter
0995: //to process
0996: //
0997: String oneParam = StringUtil.notNull(request
0998: .getParameter("next"));
0999:
1000: if (oneParam.length() > 0) {
1001: Input nextURL = new Input("next");
1002: nextURL.setType("hidden");
1003: nextURL.setDefaultValue(oneParam);
1004: response.addInput(nextURL);
1005: }
1006:
1007: oneParam = StringUtil
1008: .notNull(request.getParameter("immediate"));
1009:
1010: if (oneParam.length() > 0) {
1011: Input nextURL = new Input("immediate");
1012: nextURL.setType("hidden");
1013: nextURL.setDefaultValue(oneParam);
1014: response.addInput(nextURL);
1015: }
1016:
1017: Transition login = new Transition("processLogin", this );
1018:
1019: if (!useDB.equals("")) {
1020: login.addParam("dbContext", useDB);
1021: }
1022:
1023: response.add(login);
1024:
1025: Transition promptChangePassword = new Transition(
1026: "promptChangePassword", this );
1027:
1028: if (!useDB.equals("")) {
1029: promptChangePassword.addParam("dbContext", useDB);
1030: }
1031:
1032: response.add(promptChangePassword);
1033: response.add(new Transition("processLogout", this ));
1034:
1035: Transition promptSendPassword = new Transition(
1036: "promptSendPassword", this );
1037:
1038: if (!useDB.equals("")) {
1039: promptSendPassword.addParam("dbContext", useDB);
1040: }
1041:
1042: response.add(promptSendPassword);
1043: String registerController = this
1044: .getDefaultRegistrationController().getClass()
1045: .getName();
1046:
1047: if (registerController != null) {
1048: Transition promptRegister = new Transition();
1049: promptRegister.setControllerObject(registerController);
1050: promptRegister.setState("promptSelfRegister");
1051: promptRegister.setName("promptRegister");
1052:
1053: if (!useDB.equals("")) {
1054: promptRegister.addParam("dbContext", useDB);
1055: } else {
1056: promptRegister.addParam("dbContext", "default");
1057: }
1058:
1059: response.add(promptRegister);
1060: }
1061:
1062: Transition promptLogin = new Transition("promptLogin", this );
1063:
1064: if (!useDB.equals("")) {
1065: promptLogin.addParam("dbContext", useDB);
1066: }
1067:
1068: response.add(promptLogin);
1069:
1070: Transition showDBMenu = new Transition();
1071: showDBMenu.setName("showDBMenu");
1072: showDBMenu.addParam(Controller.CONTROLLER_PARAM_KEY, this
1073: .getDefaultRegistrationController().getClass()
1074: .getName());
1075: showDBMenu.addParam(STATE_PARAM_KEY, "showDBMenu");
1076: response.add(showDBMenu);
1077:
1078: Transition editPref = new Transition();
1079: editPref.setName("editPreferences");
1080: editPref
1081: .addParam(Controller.CONTROLLER_PARAM_KEY,
1082: "com.jcorporate.expresso.services.controller.EditUserPreference");
1083: editPref.addParam(STATE_PARAM_KEY, "edit");
1084: response.add(editPref);
1085:
1086: //
1087: //Add a revalidate email link to the login screen if the default
1088: //registration domain sends validation emails.
1089: //
1090: try {
1091: String regDomain = Setup.getValue(request.getDataContext(),
1092: "defaultRegDomain");
1093: if (regDomain != null && regDomain.length() > 0) {
1094: RegistrationDomain rd = new RegistrationDomain(
1095: Securable.SYSTEM_ACCOUNT);
1096: rd.setDataContext(request.getDataContext());
1097: rd.setField("Name", regDomain);
1098: if (rd.find()) {
1099: String emailValidate = rd.getField("EmailValidate");
1100: if (StringUtil.toBoolean(emailValidate)) {
1101: Transition revalidate = new Transition(
1102: "promptRevalidate", this );
1103: revalidate.setLabel("Resend Email Validation");
1104: response.add(revalidate);
1105: }
1106: }
1107: }
1108: } catch (DBException ex) {
1109: log.error(
1110: "Error getting default regdomain setup parameter",
1111: ex);
1112: }
1113: }
1114:
1115: /**
1116: * This function prompts for email revalidation
1117: *
1118: * @param request The <code>ControllerRequest</code> object handed to us
1119: * by the framework.
1120: * @param response The <code>ControllerResponse</code> object handed to us
1121: * by the framework
1122: * @throws ControllerException upon error
1123: */
1124: protected void runPromptRevalidateState(ControllerRequest request,
1125: ControllerResponse response) throws ControllerException {
1126: response.setTitle("Enter Email For Revalidation Request");
1127:
1128: response
1129: .addOutput(new Output(
1130: "If there was a transient error in the email delivery"
1131: + " system, you can request a resend of your email validation request."));
1132:
1133: Input i = new Input("Email", "Email Address");
1134: response.add(i);
1135:
1136: Transition t = new Transition("processRevalidate", this );
1137: t.setLabel("Resend Validation Email");
1138: response.add(t);
1139: }
1140:
1141: /**
1142: * This function processes the revalidation email request, attempts to
1143: * find the user, and if successfull, forwards the control over to the
1144: * RegistrationController to do the actual resending of the validation
1145: * email.
1146: *
1147: * @param request The <code>ControllerRequest</code> object handed to us
1148: * by the framework.
1149: * @param response The <code>ControllerResponse</code> object handed to us
1150: * by the framework
1151: * @throws ControllerException upon error
1152: * @throws NonHandleableException upon fatal error
1153: */
1154: protected void runProcessRevalidateState(ControllerRequest request,
1155: ControllerResponse response) throws ControllerException,
1156: NonHandleableException {
1157: response.setTitle("Revalidation Processing");
1158: ErrorCollection ec = request.getErrorCollection();
1159: if (ec == null) {
1160: ec = new ErrorCollection();
1161: }
1162:
1163: if (ec.getErrorCount() > 0) {
1164: response.setFormCache();
1165: response.saveErrors(ec);
1166: transition("promptRevalidate", request, response);
1167: return;
1168: }
1169:
1170: try {
1171: User u = new User();
1172: u.setDBName(request.getDataContext());
1173: u.setEmail(request.getParameter("Email"));
1174: if (!u.find()) {
1175: log.error("Received request for email address: "
1176: + request.getParameter("Email")
1177: + "but user does not exist");
1178:
1179: ec.addError("You entered an invalid email address");
1180: } else if (!"I".equals(u.getAccountStatus())) {
1181: ec
1182: .addError("This account is not waiting for email address confirmation");
1183: }
1184:
1185: if (ec.getErrorCount() > 0) {
1186: response.setFormCache();
1187: response.saveErrors(ec);
1188: transition("promptRevalidate", request, response);
1189: } else {
1190: Transition reValidate = new Transition("revalidate",
1191: "Click Here To Resend Email Validation", this
1192: .getDefaultRegistrationController()
1193: .getClass(), "processRevalidate");
1194:
1195: reValidate.addParam("Email", u.getEmail());
1196: reValidate.addParam("db", request.getDataContext());
1197: reValidate.addParam("loginController", this .getClass()
1198: .getName());
1199: reValidate.transition(request, response, false);
1200: }
1201:
1202: } catch (DBException ex) {
1203: log.error("Error processing email revalidation", ex);
1204: throw new ControllerException(
1205: "Error processing email revalidation."
1206: + " The Administrator has been notified");
1207: }
1208:
1209: }
1210:
1211: protected void runPromptSendPasswordState(
1212: ControllerRequest request, ControllerResponse response)
1213: throws ControllerException {
1214: String dbContext = StringUtil.notNull(request
1215: .getParameter("dbContext"));
1216:
1217: if (!dbContext.equals("")) {
1218: request.setDataContext(dbContext);
1219: }
1220:
1221: Input email = new Input();
1222: email.setName("Email");
1223: email.setLabel("EMail");
1224:
1225: String emailValue = StringUtil.notNull(response
1226: .getFormCache("Email"));
1227: email.setDefaultValue(emailValue);
1228: email.setDisplayLength(45);
1229: email.setMaxLength(60);
1230: response.addInput(email);
1231:
1232: Transition submit = new Transition("processSendPassword", this );
1233: submit.setLabel(response.getString("passwdResetSubject"));
1234: submit.addParam("dbContext", request.getDataContext());
1235: response.add(submit);
1236:
1237: Transition pr = new Transition("promptSelfRegister", this
1238: .getDefaultRegistrationController());
1239: pr.addParam("dbContext", request.getDataContext());
1240: response.add(pr);
1241:
1242: Transition pl = new Transition("promptLogin", this );
1243: pl.addParam("dbContext", request.getDataContext());
1244: response.add(pl);
1245: }
1246:
1247: /**
1248: * Override the normal stateAllowed method to always allow
1249: * access to this controller for certain states - otherwise no-one can ever log in :-)
1250: *
1251: * @param newState the state to transition to.
1252: * @param params The controllerRequest object
1253: * @return true if the state is allowed for the currently logged in user.
1254: * @throws ControllerException if there is an error while looking up the sercurity permissions
1255: */
1256: public synchronized boolean stateAllowed(String newState,
1257: ControllerRequest params) throws ControllerException {
1258: if (newState.equals("promptChangePW")
1259: || newState.equals("processChangePW")
1260: || newState.equals("promptLogout")) {
1261: return super .stateAllowed(newState, params);
1262: }
1263:
1264: return true;
1265: } /* stateAllowed(String) */
1266:
1267: }
|