diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java index 17287384d..085005edd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java @@ -15,7 +15,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; -import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; /** * Handle the return from the external authorization login server. If we are @@ -74,7 +73,6 @@ public class LoginExternalAuthReturn extends BaseLoginServlet { } private void removeLoginProcessArtifacts(HttpServletRequest req) { - LoginProcessBean.removeBean(req); req.getSession().removeAttribute(ATTRIBUTE_REFERRER); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index 6f0ee3cf9..3cb8116c1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -17,6 +17,7 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; /** * A user has just completed the login process. What page do we direct them to? @@ -24,14 +25,13 @@ import edu.cornell.mannlib.vitro.webapp.controller.Controllers; public class LoginRedirector { private static final Log log = LogFactory.getLog(LoginRedirector.class); - private static final String ATTRIBUTE_RETURN_FROM_FORCED_LOGIN = "return_from_forced_login"; - private final HttpServletRequest request; private final HttpServletResponse response; private final HttpSession session; - private final String urlOfRestrictedPage; private final String uriOfAssociatedIndividual; + private final String loginProcessPage; + private final String afterLoginPage; public LoginRedirector(HttpServletRequest request, HttpServletResponse response) { @@ -39,18 +39,12 @@ public class LoginRedirector { this.session = request.getSession(); this.response = response; - urlOfRestrictedPage = getUrlOfRestrictedPage(); uriOfAssociatedIndividual = getAssociatedIndividualUri(); - } - - /** Were we forced to log in when trying to access a restricted page? */ - private String getUrlOfRestrictedPage() { - String url = (String) session - .getAttribute(ATTRIBUTE_RETURN_FROM_FORCED_LOGIN); - session.removeAttribute(ATTRIBUTE_RETURN_FROM_FORCED_LOGIN); - log.debug("URL of restricted page is " + url); - return url; + LoginProcessBean processBean = LoginProcessBean.getBean(request); + log.debug("process bean is: " + processBean); + loginProcessPage = processBean.getLoginPageUrl(); + afterLoginPage = processBean.getAfterLoginUrl(); } /** Is there an Individual associated with this user? */ @@ -76,30 +70,79 @@ public class LoginRedirector { } public void redirectLoggedInUser() throws IOException { - if (isForcedFromRestrictedPage()) { - log.debug("Returning to restricted page."); - response.sendRedirect(urlOfRestrictedPage); - } else if (isUserEditorOrBetter()) { - log.debug("Going to site admin page."); - response.sendRedirect(getSiteAdminPageUrl()); - } else if (isSelfEditorWithIndividual()) { - log.debug("Going to Individual home page."); - response.sendRedirect(getAssociatedIndividualHomePage()); - } else { - log.debug("User not recognized. Going to application home."); - DisplayMessage.setMessage(request, "You have logged in, " - + "but the system contains no profile for you."); + try { + if (isSelfEditorWithIndividual()) { + log.debug("Going to Individual home page."); + response.sendRedirect(getAssociatedIndividualHomePage()); + } else if (isMerelySelfEditor()) { + log.debug("User not recognized. Going to application home."); + DisplayMessage.setMessage(request, "You have logged in, " + + "but the system contains no profile for you."); + response.sendRedirect(getApplicationHomePageUrl()); + } else { + if (hasSomeplaceToGoAfterLogin()) { + log.debug("Returning to requested page: " + afterLoginPage); + response.sendRedirect(afterLoginPage); + } else if (loginProcessPage == null) { + log.debug("Don't know what to do. Go home."); + response.sendRedirect(getApplicationHomePageUrl()); + } else if (isLoginPage(loginProcessPage)) { + log.debug("Coming from /login. Going to site admin page."); + response.sendRedirect(getSiteAdminPageUrl()); + } else { + log.debug("Coming from a login widget. Going back there."); + response.sendRedirect(loginProcessPage); + } + } + LoginProcessBean.removeBean(request); + } catch (IOException e) { + log.debug("Problem with re-direction", e); response.sendRedirect(getApplicationHomePageUrl()); } } - private boolean isForcedFromRestrictedPage() { - return urlOfRestrictedPage != null; + public void redirectCancellingUser() throws IOException { + try { + if (hasSomeplaceToGoAfterLogin()) { + log.debug("Returning to requested page: " + afterLoginPage); + response.sendRedirect(afterLoginPage); + } else if (loginProcessPage == null) { + log.debug("Don't know what to do. Go home."); + response.sendRedirect(getApplicationHomePageUrl()); + } else if (isLoginPage(loginProcessPage)) { + log.debug("Coming from /login. Going to home."); + response.sendRedirect(getApplicationHomePageUrl()); + } else { + log.debug("Coming from a login widget. Going back there."); + response.sendRedirect(loginProcessPage); + } + LoginProcessBean.removeBean(request); + } catch (IOException e) { + log.debug("Problem with re-direction", e); + response.sendRedirect(getApplicationHomePageUrl()); + } } - private boolean isUserEditorOrBetter() { - return LoginStatusBean.getBean(session).isLoggedInAtLeast( - LoginStatusBean.EDITOR); + public void redirectUnrecognizedExternalUser(String username) + throws IOException { + log.debug("Redirecting unrecognized external user: " + username); + DisplayMessage.setMessage(request, + "VIVO cannot find a profile for your account."); + response.sendRedirect(getApplicationHomePageUrl()); + } + + private boolean hasSomeplaceToGoAfterLogin() { + return afterLoginPage != null; + } + + private boolean isMerelySelfEditor() { + return LoginStatusBean.getBean(session).isLoggedInExactly( + LoginStatusBean.NON_EDITOR); + } + + private boolean isLoginPage(String page) { + return ((page != null) && page.endsWith(request.getContextPath() + + Controllers.LOGIN)); } private String getSiteAdminPageUrl() { @@ -120,14 +163,6 @@ public class LoginRedirector { } } - public void redirectUnrecognizedExternalUser(String username) - throws IOException { - log.debug("Redirecting unrecognized external user: " + username); - DisplayMessage.setMessage(request, - "VIVO cannot find a profile for your account."); - response.sendRedirect(getApplicationHomePageUrl()); - } - /** * The application home page can be overridden by an attribute in the * ServletContext. Further, it can either be an absolute URL, or it can be @@ -145,14 +180,4 @@ public class LoginRedirector { } return request.getContextPath(); } - - // ---------------------------------------------------------------------- - // static helper methods - // ---------------------------------------------------------------------- - - public static void setReturnUrlFromForcedLogin(HttpServletRequest request, - String url) { - request.getSession().setAttribute(ATTRIBUTE_RETURN_FROM_FORCED_LOGIN, - url); - } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java index c1b112990..c370360a7 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java @@ -8,6 +8,8 @@ import static edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean import static edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.State.NOWHERE; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; @@ -28,6 +30,7 @@ import com.hp.hpl.jena.ontology.OntModel; import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vitro.webapp.beans.User; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; @@ -41,6 +44,24 @@ public class Authenticate extends VitroHttpServlet { private static final Log log = LogFactory.getLog(Authenticate.class .getName()); + /** + * If this is set at any point in the process, store it as the post-login + * destination. + * + * NOTE: we expect URL-encoding on this parameter, and will decode it when + * we read it. + */ + private static final String PARAMETER_AFTER_LOGIN = "afterLogin"; + + /** + * If this is set at any point in the process, store the referrer as the + * post-login destination. + */ + private static final String PARAMETER_RETURN = "return"; + + /** If this is set, a status of NOWHERE should be treated as LOGGING_IN. */ + private static final String PARAMETER_LOGGING_IN = "loginForm"; + /** The username field on the login form. */ private static final String PARAMETER_USERNAME = "loginName"; @@ -68,6 +89,8 @@ public class Authenticate extends VitroHttpServlet { VitroRequest vreq = new VitroRequest(request); try { + recordLoginProcessPages(vreq); + // Where do we stand in the process? State entryState = getCurrentLoginState(vreq); dumpStateToLog("entry", entryState, vreq); @@ -95,7 +118,7 @@ public class Authenticate extends VitroHttpServlet { // Send them on their way. switch (exitState) { case NOWHERE: - redirectCancellingUser(vreq, response); + new LoginRedirector(vreq, response).redirectCancellingUser(); break; case LOGGING_IN: showLoginScreen(vreq, response); @@ -113,30 +136,77 @@ public class Authenticate extends VitroHttpServlet { } + /** + * If they supply an after-login page, record it and use the Login page for + * the process. + * + * If they supply a return flag, record the referrer as the after-login page + * and use the Login page for the process. + * + * Otherwise, use the current page for the process. + */ + private void recordLoginProcessPages(HttpServletRequest request) { + LoginProcessBean bean = LoginProcessBean.getBean(request); + + String afterLoginUrl = request.getParameter(PARAMETER_AFTER_LOGIN); + if (afterLoginUrl != null) { + try { + String decoded = URLDecoder.decode(afterLoginUrl, "UTF-8"); + bean.setAfterLoginUrl(decoded); + } catch (UnsupportedEncodingException e) { + log.error("Really? No UTF-8 encoding?"); + } + } + + String returnParameter = request.getParameter(PARAMETER_RETURN); + if (returnParameter != null) { + String referrer = request.getHeader("referer"); + bean.setAfterLoginUrl(referrer); + } + + if (bean.getAfterLoginUrl() != null) { + bean.setLoginPageUrl(request.getContextPath() + Controllers.LOGIN); + } else { + bean.setLoginPageUrl(request.getHeader("referer")); + } + } + /** * Where are we in the process? Logged in? Not? Somewhere in between? */ private State getCurrentLoginState(HttpServletRequest request) { + State currentState; + HttpSession session = request.getSession(false); if (session == null) { + currentState = NOWHERE; log.debug("no session: current state is NOWHERE"); - return NOWHERE; - } - - if (LoginStatusBean.getBean(request).isLoggedIn()) { + } else if (LoginStatusBean.getBean(request).isLoggedIn()) { + currentState = LOGGED_IN; log.debug("found a LoginStatusBean: current state is LOGGED IN"); - return LOGGED_IN; - } - - if (LoginProcessBean.isBean(request)) { - State state = LoginProcessBean.getBean(request).getState(); - log.debug("state from LoginProcessBean is " + state); - return state; + } else if (LoginProcessBean.isBean(request)) { + currentState = LoginProcessBean.getBean(request).getState(); + log.debug("state from LoginProcessBean is " + currentState); } else { + currentState = NOWHERE; log.debug("no LoginSessionBean, no LoginProcessBean: " + "current state is NOWHERE"); - return NOWHERE; } + + if ((currentState == NOWHERE) && isLoggingInByParameter(request)) { + currentState = LOGGING_IN; + log.debug("forced from NOWHERE to LOGGING_IN by '" + + PARAMETER_LOGGING_IN + "' parameter"); + } + + return currentState; + } + + /** + * If this parameter is present, we aren't NOWHERE. + */ + private boolean isLoggingInByParameter(HttpServletRequest request) { + return (request.getParameter(PARAMETER_LOGGING_IN) != null); } /** @@ -288,7 +358,6 @@ public class Authenticate extends VitroHttpServlet { log.debug("Completed login: " + username); getAuthenticator(request).recordLoginAgainstUserAccount(username, AuthenticationSource.INTERNAL); - LoginProcessBean.removeBean(request); } /** @@ -301,15 +370,14 @@ public class Authenticate extends VitroHttpServlet { getAuthenticator(request).recordNewPassword(username, newPassword); getAuthenticator(request).recordLoginAgainstUserAccount(username, AuthenticationSource.INTERNAL); - LoginProcessBean.removeBean(request); } /** * State change: they decided to cancel the login. */ private void transitionToNowhere(HttpServletRequest request) { + LoginProcessBean.getBean(request).setState(NOWHERE); log.debug("Cancelling login."); - LoginProcessBean.removeBean(request); } /** @@ -331,35 +399,17 @@ public class Authenticate extends VitroHttpServlet { throws IOException { log.debug("logging in."); - String referringPage = vreq.getHeader("referer"); - if (referringPage == null) { - log.warn("No referring page on the request"); - referringPage = getHomeUrl(vreq); - } - response.sendRedirect(referringPage); + String loginProcessPage = LoginProcessBean.getBean(vreq) + .getLoginPageUrl(); + response.sendRedirect(loginProcessPage); return; } - /** - * Exit: user cancelled the login, so show them the home page. - */ - private void redirectCancellingUser(HttpServletRequest request, - HttpServletResponse response) throws IOException { - log.debug("User cancelled the login. Redirect to site admin page."); - LoginProcessBean.removeBean(request); - response.sendRedirect(getHomeUrl(request)); - } - /** Get a reference to the Authenticator. */ private Authenticator getAuthenticator(HttpServletRequest request) { return Authenticator.getInstance(request); } - /** What's the URL for the home page? */ - private String getHomeUrl(HttpServletRequest request) { - return request.getContextPath(); - } - // ---------------------------------------------------------------------- // Public utility methods. // ---------------------------------------------------------------------- @@ -422,7 +472,7 @@ public class Authenticate extends VitroHttpServlet { private void dumpStateToLog(String label, State state, VitroRequest vreq) { log.debug("State on " + label + ": " + state); - + if (log.isTraceEnabled()) { log.trace("Status bean on " + label + ": " + LoginStatusBean.getBean(vreq)); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java index f7f79fd6b..21e87bc0e 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java @@ -319,7 +319,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { } urls.put("search", urlBuilder.getPortalUrl(Route.SEARCH)); urls.put("termsOfUse", urlBuilder.getPortalUrl(Route.TERMS_OF_USE)); - urls.put("login", urlBuilder.getPortalUrl(Route.LOGIN)); + urls.put("login", urlBuilder.getLoginUrl()); urls.put("logout", urlBuilder.getLogoutUrl()); urls.put("siteAdmin", urlBuilder.getPortalUrl(Route.SITE_ADMIN)); urls.put("siteIcons", urlBuilder.getPortalUrl(themeDir + "/site_icons")); // deprecated diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java index e685d4fca..6e09fddab 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java @@ -26,6 +26,7 @@ public class UrlBuilder { public enum Route { ABOUT("/about"), + AUTHENTICATE("/authenticate"), BROWSE("/browse"), CONTACT("/contact"), INDIVIDUAL("/individual"), @@ -120,6 +121,10 @@ public class UrlBuilder { return contextPath; } + public String getLoginUrl() { + return getPortalUrl(Route.AUTHENTICATE, "return", "true"); + } + public String getLogoutUrl() { return getPortalUrl(Route.LOGOUT); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java index 4c21c830a..09d15fd8b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java @@ -164,6 +164,12 @@ public class LoginProcessBean { /** What arguments are needed to format the message? */ private Object[] messageArguments = NO_ARGUMENTS; + /** Where is the interaction taking place? */ + private String loginPageUrl; + + /** Where do we go when finished? */ + private String afterLoginUrl; + /** * What username was submitted to the form? This isn't just for display -- * if they are changing passwords, we need to remember who it is. @@ -214,12 +220,29 @@ public class LoginProcessBean { this.username = username; } + public String getLoginPageUrl() { + return loginPageUrl; + } + + public void setLoginPageUrl(String loginPageUrl) { + this.loginPageUrl = loginPageUrl; + } + + public String getAfterLoginUrl() { + return afterLoginUrl; + } + + public void setAfterLoginUrl(String afterLoginUrl) { + this.afterLoginUrl = afterLoginUrl; + } + @Override public String toString() { return "LoginProcessBean[state=" + currentState + ", message=" + message + ", messageArguments=" + Arrays.deepToString(messageArguments) + ", username=" - + username + "]"; + + username + ", loginPageUrl=" + loginPageUrl + + ", afterLoginUrl=" + afterLoginUrl + "]"; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java index 665cdfa65..18f96d8fc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java @@ -106,14 +106,11 @@ public class LoginWidget extends Widget { } /** - * User is just starting the login process. Be sure that we have a - * {@link LoginProcessBean} with the correct status. Show them the login - * screen. + * User is starting the login process. Show them the login screen. */ private WidgetTemplateValues showLoginScreen(HttpServletRequest request) throws IOException { LoginProcessBean bean = LoginProcessBean.getBean(request); - bean.setState(State.LOGGING_IN); log.trace("Going to login screen: " + bean); WidgetTemplateValues values = new WidgetTemplateValues(Macro.LOGIN.toString()); @@ -150,7 +147,6 @@ public class LoginWidget extends Widget { */ private WidgetTemplateValues showPasswordChangeScreen(HttpServletRequest request) { LoginProcessBean bean = LoginProcessBean.getBean(request); - bean.setState(State.FORCED_PASSWORD_CHANGE); log.trace("Going to password change screen: " + bean); WidgetTemplateValues values = new WidgetTemplateValues( diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java index 2b4049242..ebcbc6323 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java @@ -9,6 +9,7 @@ import static org.junit.Assert.fail; import java.net.URL; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -17,6 +18,9 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import stubs.javax.servlet.ServletConfigStub; import stubs.javax.servlet.ServletContextStub; @@ -27,38 +31,204 @@ import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.beans.User; -import edu.cornell.mannlib.vitro.webapp.controller.Controllers; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.AuthenticatorStub; -import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginRedirector; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.State; /** - * Test the Authentate class. + *
+ * Test the Authenticate class.
+ * 
+ * This uses parameterized unit tests. Several sets of test data are set up, and
+ * then each test is run with each set of data.
+ * 
+ * Each set of test data includes 
+ *   information about the user who is logging in, 
+ *   information about how the user began the login process
+ *   information about where the user should end up
+ *   
+ * We run the tests with these users:
+ *   A DBA who has never logged in before
+ *   A DBA who has logged in before
+ *   A self-editor who has logged in before
+ *   A self-editor wannabe, who has never logged in and has no profile.
+ * 
+ * We run the tests with the assumption that the user started from:
+ *   The login page
+ *   A page that holds the login widget
+ *   A forced login
+ *   A login link on some page
+ * 
*/ +@RunWith(value = Parameterized.class) public class AuthenticateTest extends AbstractTestClass { - private static final String USER_DBA_NAME = "dbaName"; - private static final String USER_DBA_URI = "dbaURI"; - private static final String USER_DBA_PASSWORD = "dbaPassword"; - private static final String USER_OLDHAND_NAME = "oldHandName"; - private static final String USER_OLDHAND_URI = "oldHandURI"; - private static final String USER_OLDHAND_PASSWORD = "oldHandPassword"; - private static final int USER_OLDHAND_LOGIN_COUNT = 100; + // ---------------------------------------------------------------------- + // Helper classes + // ---------------------------------------------------------------------- - private static final String URL_LOGIN_PAGE = "http://my.local.site/vivo/" - + Controllers.LOGIN; - private static final String URL_SITE_ADMIN_PAGE = Controllers.SITE_ADMIN; + private static class UserInfo { + final String username; + final String uri; + final String password; + final int securityLevel; + final int loginCount; - private static final String URL_HOME_PAGE = ""; - private static final String URL_SESSION_REDIRECT = "/sessionRedirect"; - private static final String URL_CONTEXT_REDIRECT_LOCAL = "/servletContextRedirect"; - private static final String URL_CONTEXT_REDIRECT_REMOTE = "http://servletContextRedirect"; - private static final String URL_SELF_EDITOR_PAGE = "/individual?uri=selfEditorURI"; + public UserInfo(String username, String uri, String password, + int securityLevel, int loginCount) { + this.username = username; + this.uri = uri; + this.password = password; + this.securityLevel = securityLevel; + this.loginCount = loginCount; + } - private static final LoginStatusBean LOGIN_STATUS_DBA = new LoginStatusBean( - USER_DBA_URI, USER_DBA_NAME, LoginStatusBean.DBA, - AuthenticationSource.INTERNAL); + @Override + public String toString() { + return "UserInfo[username=" + username + ", uri=" + uri + + ", password=" + password + ", securityLevel=" + + securityLevel + ", loginCount=" + loginCount + "]"; + } + } + + private static class HowDidWeGetHere { + final String afterLoginUrl; + final boolean returnParameterSet; + final String referrer; + + public HowDidWeGetHere(String afterLoginUrl, + boolean returnParameterSet, String referrer) { + this.afterLoginUrl = afterLoginUrl; + this.returnParameterSet = returnParameterSet; + this.referrer = referrer; + } + + @Override + public String toString() { + return "HowDidWeGetHere[afterLoginUrl=" + afterLoginUrl + + ", returnParameterSet=" + returnParameterSet + + ", referrer=" + referrer + "]"; + } + } + + private static class WhereTo { + final String expectedContinueUrl; + final String expectedCompletionUrl; + final String expectedCancelUrl; + + public WhereTo(String expectedContinueUrl, + String expectedCompletionUrl, String expectedCancelUrl) { + this.expectedContinueUrl = expectedContinueUrl; + this.expectedCompletionUrl = expectedCompletionUrl; + this.expectedCancelUrl = expectedCancelUrl; + } + + @Override + public String toString() { + return "WhereTo[expectedContinueUrl=" + expectedContinueUrl + + ", expectedCompletionUrl=" + expectedCompletionUrl + + ", expectedCancelUrl=" + expectedCancelUrl + "]"; + } + } + + // ---------------------------------------------------------------------- + // The parameters + // ---------------------------------------------------------------------- + + // --------- Pages ---------- + + /** the login page */ + private static final String URL_LOGIN = "/vivo/login"; + + /** some page with a login widget on it. */ + private static final String URL_WIDGET = "/vivo/widgetPage"; + + /** a restricted page that forces a login. */ + private static final String URL_RESTRICTED = "/vivo/otherPage"; + + /** a page with a login link. */ + private static final String URL_LINK = "/vivo/linkPage"; + + // pages that we might end up on. + private static final String URL_HOME = "/vivo"; + private static final String URL_SITE_ADMIN = "/vivo/siteAdmin"; + private static final String URL_SELF_PROFILE = "/vivo/individual?uri=old_self_associated_uri"; + + // --------- Users ---------- + + /** A DBA who has never logged in (forces password change). */ + private static final UserInfo NEW_DBA = new UserInfo("new_dba_name", + "new_dba_uri", "new_dba_pw", 50, 0); + + /** A DBA who has logged in before. */ + private static final UserInfo OLD_DBA = new UserInfo("old_dba_name", + "old_dba_uri", "old_dba_pw", 50, 5); + + /** A self-editor who has logged in before and has a profile. */ + private static final UserInfo OLD_SELF = new UserInfo("old_self_name", + "old_self_uri", "old_self_pw", 1, 100); + + /** A self-editor who has never logged in and has no profile. */ + private static final UserInfo NEW_STRANGER = new UserInfo( + "new_stranger_name", "new_stranger_uri", "stranger_pw", 1, 0); + + // --------- Starting circumstances ---------- + + private static final HowDidWeGetHere FROM_FORCED = new HowDidWeGetHere( + URL_RESTRICTED, false, URL_RESTRICTED); + + private static final HowDidWeGetHere FROM_LINK = new HowDidWeGetHere(null, + true, URL_LINK); + + private static final HowDidWeGetHere FROM_WIDGET = new HowDidWeGetHere( + null, false, URL_WIDGET); + + private static final HowDidWeGetHere FROM_LOGIN = new HowDidWeGetHere(null, + false, URL_LOGIN); + + // --------- All sets of test data ---------- + + @Parameters + public static Collection data() { + Object[][] data = new Object[][] { + { NEW_DBA, FROM_FORCED, + new WhereTo(URL_LOGIN, URL_RESTRICTED, URL_RESTRICTED) }, // 0 + { NEW_DBA, FROM_LINK, + new WhereTo(URL_LOGIN, URL_LINK, URL_LINK) }, // 1 + { NEW_DBA, FROM_WIDGET, + new WhereTo(URL_WIDGET, URL_WIDGET, URL_WIDGET) }, // 2 + { NEW_DBA, FROM_LOGIN, + new WhereTo(URL_LOGIN, URL_SITE_ADMIN, URL_HOME) }, // 3 + { OLD_DBA, FROM_FORCED, + new WhereTo(URL_LOGIN, URL_RESTRICTED, null) }, // 4 + { OLD_DBA, FROM_LINK, new WhereTo(URL_LOGIN, URL_LINK, null) }, // 5 + { OLD_DBA, FROM_WIDGET, + new WhereTo(URL_WIDGET, URL_WIDGET, null) }, // 6 + { OLD_DBA, FROM_LOGIN, + new WhereTo(URL_LOGIN, URL_SITE_ADMIN, null) }, // 7 + { OLD_SELF, FROM_FORCED, + new WhereTo(URL_LOGIN, URL_SELF_PROFILE, null) }, // 8 + { OLD_SELF, FROM_LINK, + new WhereTo(URL_LOGIN, URL_SELF_PROFILE, null) }, // 9 + { OLD_SELF, FROM_WIDGET, + new WhereTo(URL_WIDGET, URL_SELF_PROFILE, null) }, // 10 + { OLD_SELF, FROM_LOGIN, + new WhereTo(URL_LOGIN, URL_SELF_PROFILE, null) }, // 11 + { NEW_STRANGER, FROM_FORCED, + new WhereTo(URL_LOGIN, URL_HOME, URL_RESTRICTED) }, // 12 + { NEW_STRANGER, FROM_LINK, + new WhereTo(URL_LOGIN, URL_HOME, URL_LINK) }, // 13 + { NEW_STRANGER, FROM_WIDGET, + new WhereTo(URL_WIDGET, URL_HOME, URL_WIDGET) }, // 14 + { NEW_STRANGER, FROM_LOGIN, + new WhereTo(URL_LOGIN, URL_HOME, URL_HOME) } // 15 + }; + return Arrays.asList(data); + } + + // ---------------------------------------------------------------------- + // Instance variables and setup + // ---------------------------------------------------------------------- private AuthenticatorStub authenticator; private ServletContextStub servletContext; @@ -68,12 +238,23 @@ public class AuthenticateTest extends AbstractTestClass { private HttpServletResponseStub response; private Authenticate auth; + private final UserInfo userInfo; + private final HowDidWeGetHere urlBundle; + private final WhereTo whereTo; + + public AuthenticateTest(UserInfo userInfo, HowDidWeGetHere urlBundle, + WhereTo whereTo) { + this.userInfo = userInfo; + this.urlBundle = urlBundle; + this.whereTo = whereTo; + } + @Before public void setup() throws Exception { authenticator = AuthenticatorStub.setup(); - - authenticator.addUser(createNewDbaUser()); - authenticator.addUser(createOldHandUser()); + authenticator.addUser(createUserFromUserInfo()); + authenticator.setAssociatedUri(OLD_SELF.username, + "old_self_associated_uri"); servletContext = new ServletContextStub(); @@ -85,270 +266,246 @@ public class AuthenticateTest extends AbstractTestClass { request = new HttpServletRequestStub(); request.setSession(session); - request.setRequestUrl(new URL("http://this.that/vivo/siteAdmin")); + request.setRequestUrl(new URL("http://this.that/vivo/authenticate")); request.setMethod("POST"); - request.setHeader("referer", URL_LOGIN_PAGE); + request.setHeader("referer", urlBundle.referrer); + if (urlBundle.afterLoginUrl != null) { + request.addParameter("afterLogin", urlBundle.afterLoginUrl); + } + if (urlBundle.returnParameterSet) { + request.addParameter("return", ""); + } response = new HttpServletResponseStub(); auth = new Authenticate(); auth.init(servletConfig); - } - private User createNewDbaUser() { + private User createUserFromUserInfo() { User user = new User(); - user.setUsername(USER_DBA_NAME); - user.setURI(USER_DBA_URI); - user.setRoleURI("50"); - user.setMd5password(Authenticate.applyMd5Encoding(USER_DBA_PASSWORD)); - user.setFirstTime(null); - user.setLoginCount(0); - return user; - } - - private User createOldHandUser() { - User user = new User(); - user.setUsername(USER_OLDHAND_NAME); - user.setURI(USER_OLDHAND_URI); - user.setRoleURI("1"); - user.setMd5password(Authenticate - .applyMd5Encoding(USER_OLDHAND_PASSWORD)); - user.setLoginCount(USER_OLDHAND_LOGIN_COUNT); - user.setFirstTime(new Date(0)); + user.setUsername(userInfo.username); + user.setURI(userInfo.uri); + user.setRoleURI(String.valueOf(userInfo.securityLevel)); + user.setMd5password(Authenticate.applyMd5Encoding(userInfo.password)); + user.setLoginCount(userInfo.loginCount); + if (userInfo.loginCount > 0) { + user.setFirstTime(new Date(0)); + } return user; } // ---------------------------------------------------------------------- - // the tests + // The tests // ---------------------------------------------------------------------- - @Test - public void alreadyLoggedIn() { - LoginStatusBean.setBean(session, LOGIN_STATUS_DBA); - - auth.doPost(request, response); - - assertExpectedRedirect(URL_SITE_ADMIN_PAGE); - assertNoProcessBean(); - assertExpectedLoginSessions(); - } - @Test public void justGotHere() { auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(LOGGING_IN, "", "", ""); + assertProcessBean(LOGGING_IN, "", "", ""); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); } @Test public void loggingInNoUsername() { - setProcessBean(LOGGING_IN); + setProcessBean(LOGGING_IN, null); auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(LOGGING_IN, "", "", + assertProcessBean(LOGGING_IN, "", "", "Please enter your email address."); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); } @Test public void loggingInUsernameNotRecognized() { - setProcessBean(LOGGING_IN); + setProcessBean(LOGGING_IN, null); setLoginNameAndPassword("unknownBozo", null); auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(LOGGING_IN, "unknownBozo", "", + assertProcessBean(LOGGING_IN, "unknownBozo", "", "The email or password you entered is incorrect."); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); } @Test public void loggingInNoPassword() { - setProcessBean(LOGGING_IN); - setLoginNameAndPassword(USER_DBA_NAME, null); + setProcessBean(LOGGING_IN, null); + setLoginNameAndPassword(userInfo.username, null); auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(LOGGING_IN, USER_DBA_NAME, "", + assertProcessBean(LOGGING_IN, userInfo.username, "", "Please enter your password."); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); } @Test public void loggingInPasswordIsIncorrect() { - setProcessBean(LOGGING_IN); - setLoginNameAndPassword(USER_DBA_NAME, "bogus_password"); + setProcessBean(LOGGING_IN, null); + setLoginNameAndPassword(userInfo.username, "bogus_password"); auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(LOGGING_IN, USER_DBA_NAME, "", + assertProcessBean(LOGGING_IN, userInfo.username, "", "The email or password you entered is incorrect."); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); } @Test - public void loggingInSuccessfulNotFirstTime() { - setProcessBean(LOGGING_IN); - setLoginNameAndPassword(USER_OLDHAND_NAME, USER_OLDHAND_PASSWORD); + public void loggingInSuccessful() { + if (userInfo.loginCount == 0) { + testLoginFirstTime(); + } else { + testLoginNotFirstTime(); + } + } + + private void testLoginFirstTime() { + setProcessBean(LOGGING_IN, null); + setLoginNameAndPassword(userInfo.username, userInfo.password); + + auth.doPost(request, response); + + assertProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username, "", ""); + assertNewLoginSessions(); + assertRedirectToContinueUrl(); + } + + private void testLoginNotFirstTime() { + setProcessBean(LOGGING_IN, null); + setLoginNameAndPassword(userInfo.username, userInfo.password); auth.doPost(request, response); assertNoProcessBean(); - assertExpectedRedirect(URL_HOME_PAGE); - assertExpectedLoginSessions(USER_OLDHAND_NAME); - } - - // ---------------------------------------------------------------------- - // first-time password change - // ---------------------------------------------------------------------- - - @Test - public void loggingInSuccessfulFirstTime() { - setProcessBean(LOGGING_IN); - setLoginNameAndPassword(USER_DBA_NAME, USER_DBA_PASSWORD); - - auth.doPost(request, response); - - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME, "", ""); + assertNewLoginSessions(userInfo.username); + assertRedirectToCompletionUrl(); } @Test public void changingPasswordCancel() { - setProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME); - request.addParameter("cancel", "true"); + // Only valid for first-time login. + if (userInfo.loginCount == 0) { + setProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username); + request.addParameter("cancel", "true"); - auth.doPost(request, response); + auth.doPost(request, response); - assertExpectedRedirect(URL_HOME_PAGE); - assertExpectedLoginSessions(); - assertNoProcessBean(); + assertNoProcessBean(); + assertNewLoginSessions(); + assertRedirectToCancelUrl(); + } } @Test public void changingPasswordWrongLength() { - setProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME); - setNewPasswordAttempt("HI", "HI"); + // Only valid for first-time login. + if (userInfo.loginCount == 0) { + setProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username); + setNewPasswordAttempt("HI", "HI"); - auth.doPost(request, response); + auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME, "", - "Please enter a password between 6 and 12 characters in length."); + assertRedirectToContinueUrl(); + assertNewLoginSessions(); + assertProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username, "", + "Please enter a password between 6 and 12 characters in length."); + } } @Test public void changingPasswordDontMatch() { - setProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME); - setNewPasswordAttempt("LongEnough", "DoesNotMatch"); + // Only valid for first-time login. + if (userInfo.loginCount == 0) { + setProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username); + setNewPasswordAttempt("LongEnough", "DoesNotMatch"); - auth.doPost(request, response); + auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME, "", - "The passwords entered do not match."); + assertRedirectToContinueUrl(); + assertNewLoginSessions(); + assertProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username, "", + "The passwords entered do not match."); + } } @Test public void changingPasswordSameAsBefore() { - setProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME); - setNewPasswordAttempt(USER_DBA_PASSWORD, USER_DBA_PASSWORD); + // Only valid for first-time login. + if (userInfo.loginCount == 0) { + setProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username); + setNewPasswordAttempt(userInfo.password, userInfo.password); - auth.doPost(request, response); + auth.doPost(request, response); - assertExpectedRedirect(URL_LOGIN_PAGE); - assertExpectedLoginSessions(); - assertExpectedProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME, "", - "Please choose a different password from the " - + "temporary one provided initially."); + assertRedirectToContinueUrl(); + assertNewLoginSessions(); + assertProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username, "", + "Please choose a different password from the " + + "temporary one provided initially."); + } } @Test public void changingPasswordSuccess() { - setProcessBean(FORCED_PASSWORD_CHANGE, USER_DBA_NAME); - setNewPasswordAttempt("NewPassword", "NewPassword"); + // Only valid for first-time login. + if (userInfo.loginCount == 0) { + setProcessBean(FORCED_PASSWORD_CHANGE, userInfo.username); + setNewPasswordAttempt("NewPassword", "NewPassword"); + + auth.doPost(request, response); + + assertRedirectToCompletionUrl(); + assertNewLoginSessions(userInfo.username); + assertNoProcessBean(); + assertPasswordChanges(userInfo.username, "NewPassword"); + } + } + + @Test + public void alreadyLoggedIn() { + LoginStatusBean statusBean = new LoginStatusBean(userInfo.uri, + userInfo.username, userInfo.securityLevel, + AuthenticationSource.INTERNAL); + LoginStatusBean.setBean(session, statusBean); auth.doPost(request, response); + assertRedirectToCompletionUrl(); assertNoProcessBean(); - assertExpectedRedirect(URL_SITE_ADMIN_PAGE); - assertExpectedLoginSessions(USER_DBA_NAME); - assertExpectedPasswordChanges(USER_DBA_NAME, "NewPassword"); + assertNewLoginSessions(); } // ---------------------------------------------------------------------- - // Assorted redirects: these assume a successful non-first-time login. + // Helper methods // ---------------------------------------------------------------------- - @Test - public void redirectReturnToRestrictedPage() { - LoginRedirector.setReturnUrlFromForcedLogin(request, - URL_SESSION_REDIRECT); - loginNotFirstTime(); - assertExpectedLiteralRedirect(URL_SESSION_REDIRECT); - } - - @Test - public void redirectDbaToSiteAdmin() { - authenticator.getUserByUsername(USER_OLDHAND_NAME).setRoleURI("50"); - loginNotFirstTime(); - assertExpectedRedirect(URL_SITE_ADMIN_PAGE); - } - - @Test - public void redirectSelfEditor() { - authenticator.setAssociatedUri(USER_OLDHAND_NAME, "selfEditorURI"); - loginNotFirstTime(); - assertExpectedRedirect(URL_SELF_EDITOR_PAGE); - } - - @Test - public void redirectUnrecognizedUserToHome() { - loginNotFirstTime(); - assertExpectedRedirect(URL_HOME_PAGE); - } - - @Test - public void redirectUnrecognizedUserToApplicationHome() { - servletContext.setAttribute("postLoginRequest", - URL_CONTEXT_REDIRECT_LOCAL); - loginNotFirstTime(); - assertExpectedRedirect(URL_CONTEXT_REDIRECT_LOCAL); - } - - @Test - public void redirectUnrecognizedUserToApplicationExternalHome() { - servletContext.setAttribute("postLoginRequest", - URL_CONTEXT_REDIRECT_REMOTE); - loginNotFirstTime(); - assertExpectedLiteralRedirect(URL_CONTEXT_REDIRECT_REMOTE); - } - - // ---------------------------------------------------------------------- - // helper methods - // ---------------------------------------------------------------------- - - private void setProcessBean(State state) { - LoginProcessBean processBean = new LoginProcessBean(); - processBean.setState(state); - LoginProcessBean.setBean(request, processBean); - } - private void setProcessBean(State state, String username) { LoginProcessBean processBean = new LoginProcessBean(); processBean.setState(state); - processBean.setUsername(username); + if (username != null) { + processBean.setUsername(username); + } + + // the urls come directly from the url bundle every time. + if (urlBundle.afterLoginUrl != null) { + processBean.setAfterLoginUrl(urlBundle.afterLoginUrl); + processBean.setLoginPageUrl(URL_LOGIN); + } else if (urlBundle.returnParameterSet) { + processBean.setAfterLoginUrl(urlBundle.referrer); + processBean.setLoginPageUrl(URL_LOGIN); + } else { + processBean.setAfterLoginUrl(null); + processBean.setLoginPageUrl(urlBundle.referrer); + } LoginProcessBean.setBean(request, processBean); } @@ -363,21 +520,28 @@ public class AuthenticateTest extends AbstractTestClass { request.addParameter("confirmPassword", confirmPassword); } - private void assertExpectedRedirect(String path) { + private void assertRedirectToContinueUrl() { + assertRedirect(whereTo.expectedContinueUrl); + } + + private void assertRedirectToCompletionUrl() { + assertRedirect(whereTo.expectedCompletionUrl); + } + + private void assertRedirectToCancelUrl() { + assertRedirect(whereTo.expectedCancelUrl); + } + + private void assertRedirect(String path) { if (path.startsWith("http://")) { assertEquals("absolute redirect", path, response.getRedirectLocation()); } else { - assertEquals("relative redirect", request.getContextPath() + path, + assertEquals("relative redirect", path, response.getRedirectLocation()); } } - /** This is for explicit redirect URLs that already include context. */ - private void assertExpectedLiteralRedirect(String path) { - assertEquals("redirect", path, response.getRedirectLocation()); - } - private void assertNoProcessBean() { if (LoginProcessBean.isBean(request)) { fail("Process bean: expected , but was <" @@ -385,7 +549,7 @@ public class AuthenticateTest extends AbstractTestClass { } } - private void assertExpectedProcessBean(State state, String username, + private void assertProcessBean(State state, String username, String infoMessage, String errorMessage) { if (!LoginProcessBean.isBean(request)) { fail("login process bean is null"); @@ -396,9 +560,25 @@ public class AuthenticateTest extends AbstractTestClass { assertEquals("error message", errorMessage, bean.getErrorMessageAndClear()); assertEquals("username", username, bean.getUsername()); + + // This should represent the URL bundle, every time. + String expectedAfterLoginUrl = (urlBundle.returnParameterSet) ? urlBundle.referrer + : urlBundle.afterLoginUrl; + assertEquals("after login URL", expectedAfterLoginUrl, + bean.getAfterLoginUrl()); } - private void assertExpectedPasswordChanges(String... strings) { + /** What logins were completed in this test? */ + private void assertNewLoginSessions(String... usernames) { + Set expected = new HashSet(Arrays.asList(usernames)); + + Set actualRecorded = new HashSet( + authenticator.getRecordedLoginUsernames()); + assertEquals("recorded logins", expected, actualRecorded); + } + + /** What passwords were changed in this test? */ + private void assertPasswordChanges(String... strings) { if ((strings.length % 2) != 0) { throw new RuntimeException( "supply even number of args: username and password"); @@ -413,26 +593,6 @@ public class AuthenticateTest extends AbstractTestClass { authenticator.getNewPasswordMap()); } - /** How many folks logged in? */ - private void assertExpectedLoginSessions(String... usernames) { - Set expected = new HashSet(Arrays.asList(usernames)); - - Set actualRecorded = new HashSet( - authenticator.getRecordedLoginUsernames()); - assertEquals("recorded logins", expected, actualRecorded); - } - - /** Boilerplate login process for the redirect tests. */ - private void loginNotFirstTime() { - setProcessBean(LOGGING_IN); - setLoginNameAndPassword(USER_OLDHAND_NAME, USER_OLDHAND_PASSWORD); - - auth.doPost(request, response); - - assertExpectedLoginSessions(USER_OLDHAND_NAME); - assertNoProcessBean(); - } - @SuppressWarnings("unused") private void showBeans() { LoginProcessBean processBean = (LoginProcessBean.isBean(request)) ? LoginProcessBean