NIHVIVO-2696 Create the Create Account page for externally authenticated users who are logging in for the first time.

This commit is contained in:
j2blake 2011-06-13 14:17:59 +00:00
parent e8ee90e9ba
commit 11a048605b
7 changed files with 438 additions and 0 deletions

View file

@ -129,6 +129,7 @@ public abstract class UserAccountsPage {
map.put("myAccount", UrlBuilder.getUrl("/accounts/myAccount"));
map.put("createPassword", UrlBuilder.getUrl("/accounts/createPassword"));
map.put("resetPassword", UrlBuilder.getUrl("/accounts/resetPassword"));
map.put("firstTimeExternal", UrlBuilder.getUrl("/accounts/firstTimeExternal"));
return map;
}

View file

@ -0,0 +1,194 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.accounts.user;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSetsLoader;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
/**
* Handle the first-time login of an Externally Authenticated user who has no
* UserAccount - let's create one!
*
* If they get here from the login, there should an externalAuthId waiting in
* the session. Otherwise, they should get here by submitting the form, which
* will have the externalAuthId as a hidden field.
*/
public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
private static final String PARAMETER_SUBMIT = "submit";
private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId";
private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress";
private static final String PARAMETER_FIRST_NAME = "firstName";
private static final String PARAMETER_LAST_NAME = "lastName";
private static final String ERROR_NO_EMAIL = "errorEmailIsEmpty";
private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse";
private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat";
private static final String ERROR_NO_FIRST_NAME = "errorFirstNameIsEmpty";
private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty";
private static final String TEMPLATE_NAME = "userAccounts-firstTimeExternal.ftl";
private static final String ATTRIBUTE_EXTERNAL_AUTH_ID = UserAccountsFirstTimeExternalPage.class
.getName();
/**
* Let some other request set the External Auth ID before redirecting to
* here.
*/
public static void setExternalAuthId(HttpServletRequest req,
String externalAuthId) {
req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID,
externalAuthId);
}
private final UserAccountsFirstTimeExternalPageStrategy strategy;
private boolean submit = false;
private String externalAuthId = "";
private String emailAddress = "";
private String firstName = "";
private String lastName = "";
private String errorCode = "";
private String bogusMessage = "";
protected UserAccountsFirstTimeExternalPage(VitroRequest vreq) {
super(vreq);
this.strategy = UserAccountsFirstTimeExternalPageStrategy.getInstance(
vreq, this, isEmailEnabled());
checkSessionForExternalAuthId();
if (externalAuthId.isEmpty()) {
parseRequestParameters();
}
validateExternalAuthId();
if (isSubmit() && !isBogus()) {
validateParameters();
}
}
private void checkSessionForExternalAuthId() {
HttpSession session = vreq.getSession();
Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID);
session.removeAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID);
if (o instanceof String) {
externalAuthId = (String) o;
}
}
private void parseRequestParameters() {
submit = isFlagOnRequest(PARAMETER_SUBMIT);
externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, "");
emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, "");
firstName = getStringParameter(PARAMETER_FIRST_NAME, "");
lastName = getStringParameter(PARAMETER_LAST_NAME, "");
}
private void validateExternalAuthId() {
if (externalAuthId.isEmpty()) {
bogusMessage = "Login failed - External ID is not found.";
return;
}
if (null != userAccountsDao
.getUserAccountByExternalAuthId(externalAuthId)) {
bogusMessage = "User account already exists for '" + externalAuthId
+ "'";
return;
}
}
public boolean isBogus() {
return !bogusMessage.isEmpty();
}
public String getBogusMessage() {
return bogusMessage;
}
public boolean isSubmit() {
return submit;
}
private void validateParameters() {
if (firstName.isEmpty()) {
errorCode = ERROR_NO_FIRST_NAME;
} else if (lastName.isEmpty()) {
errorCode = ERROR_NO_LAST_NAME;
} else if (emailAddress.isEmpty()) {
errorCode = ERROR_NO_EMAIL;
} else if (isEmailInUse()) {
errorCode = ERROR_EMAIL_IN_USE;
} else if (!isEmailValidFormat()) {
errorCode = ERROR_EMAIL_INVALID_FORMAT;
}
}
private boolean isEmailInUse() {
return userAccountsDao.getUserAccountByEmail(emailAddress) != null;
}
private boolean isEmailValidFormat() {
return Authenticator.isValidEmailAddress(emailAddress);
}
public boolean isValid() {
return errorCode.isEmpty();
}
public final ResponseValues showPage() {
Map<String, Object> body = new HashMap<String, Object>();
body.put("emailAddress", emailAddress);
body.put("firstName", firstName);
body.put("lastName", lastName);
body.put("externalAuthId", externalAuthId);
body.put("formUrls", buildUrlsMap());
if (!errorCode.isEmpty()) {
body.put(errorCode, Boolean.TRUE);
}
strategy.addMoreBodyValues(body);
return new TemplateResponseValues(TEMPLATE_NAME, body);
}
public UserAccount createAccount() {
UserAccount u = new UserAccount();
u.setEmailAddress(emailAddress);
u.setFirstName(firstName);
u.setLastName(lastName);
u.setExternalAuthId(externalAuthId);
u.setPasswordChangeRequired(false);
u.setPasswordLinkExpires(0);
u.setLoginCount(0);
u.setStatus(Status.ACTIVE);
u.setPermissionSetUris(Collections
.singleton(PermissionSetsLoader.URI_SELF_EDITOR));
userAccountsDao.insertUserAccount(u);
strategy.notifyUser(u);
return u;
}
}

View file

@ -0,0 +1,107 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.accounts.user;
import static javax.mail.Message.RecipientType.TO;
import java.util.HashMap;
import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage;
import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory;
import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage;
/**
* Handle the variations in the UserAccountsFirstTimeExternal page. If email is
* available, inform the template, and send a notification to the user.
*
* If not, then don't.
*/
public abstract class UserAccountsFirstTimeExternalPageStrategy extends
UserAccountsPage {
public static UserAccountsFirstTimeExternalPageStrategy getInstance(
VitroRequest vreq, UserAccountsFirstTimeExternalPage page,
boolean emailEnabled) {
if (emailEnabled) {
return new EmailStrategy(vreq, page);
} else {
return new NoEmailStrategy(vreq, page);
}
}
@SuppressWarnings("unused")
private UserAccountsFirstTimeExternalPage page;
public UserAccountsFirstTimeExternalPageStrategy(VitroRequest vreq,
UserAccountsFirstTimeExternalPage page) {
super(vreq);
this.page = page;
}
public abstract void addMoreBodyValues(Map<String, Object> body);
public abstract void notifyUser(UserAccount ua);
// ----------------------------------------------------------------------
// Strategy to use if email is enabled.
// ----------------------------------------------------------------------
public static class EmailStrategy extends
UserAccountsFirstTimeExternalPageStrategy {
public EmailStrategy(VitroRequest vreq,
UserAccountsFirstTimeExternalPage page) {
super(vreq, page);
}
@Override
public void addMoreBodyValues(Map<String, Object> body) {
body.put("emailIsEnabled", Boolean.TRUE);
}
@Override
public void notifyUser(UserAccount ua) {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", ua);
body.put("subjectLine", "Your VIVO account has been created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, ua.getEmailAddress());
email.setSubject("Your VIVO account has been created.");
email.setHtmlTemplate("userAccounts-firstTimeExternalEmail-html.ftl");
email.setTextTemplate("userAccounts-firstTimeExternalEmail-text.ftl");
email.setBodyMap(body);
email.send();
}
}
// ----------------------------------------------------------------------
// Strategy to use if email is disabled.
// ----------------------------------------------------------------------
public static class NoEmailStrategy extends
UserAccountsFirstTimeExternalPageStrategy {
public NoEmailStrategy(VitroRequest vreq,
UserAccountsFirstTimeExternalPage page) {
super(vreq, page);
}
@Override
public void addMoreBodyValues(Map<String, Object> body) {
// Nothing to add.
}
@Override
public void notifyUser(UserAccount ua) {
// No way to notify.
}
}
}

View file

@ -2,16 +2,23 @@
package edu.cornell.mannlib.vitro.webapp.controller.accounts.user;
import static edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource.EXTERNAL;
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.auth.requestedAction.Actions;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.EditOwnAccount;
import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginRedirector;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
/**
* Parcel out the different actions required of the UserAccounts GUI.
@ -25,6 +32,7 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
private static final String ACTION_CREATE_PASSWORD = "/createPassword";
private static final String ACTION_RESET_PASSWORD = "/resetPassword";
private static final String ACTION_MY_ACCOUNT = "/myAccount";
private static final String ACTION_FIRST_TIME_EXTERNAL = "/firstTimeExternal";
@Override
protected Actions requiredActions(VitroRequest vreq) {
@ -52,6 +60,8 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
return handleCreatePasswordRequest(vreq);
} else if (ACTION_RESET_PASSWORD.equals(action)) {
return handleResetPasswordRequest(vreq);
} else if (ACTION_FIRST_TIME_EXTERNAL.equals(action)) {
return handleFirstTimeLoginFromExternalAccount(vreq);
} else {
return handleInvalidRequest(vreq);
}
@ -95,6 +105,24 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
}
private ResponseValues handleFirstTimeLoginFromExternalAccount(
VitroRequest vreq) {
UserAccountsFirstTimeExternalPage page = new UserAccountsFirstTimeExternalPage(
vreq);
if (page.isBogus()) {
return showHomePage(vreq, page.getBogusMessage());
} else if (page.isSubmit() && page.isValid()) {
UserAccount userAccount = page.createAccount();
Authenticator auth = Authenticator.getInstance(vreq);
auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL);
LoginProcessBean.removeBean(vreq);
return showLoginRedirection(vreq);
} else {
return page.showPage();
}
}
private ResponseValues handleInvalidRequest(VitroRequest vreq) {
return showHomePage(vreq, BOGUS_STANDARD_MESSAGE);
}
@ -104,4 +132,10 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
return new RedirectResponseValues("/");
}
private ResponseValues showLoginRedirection(VitroRequest vreq) {
LoginRedirector lr = new LoginRedirector(vreq);
DisplayMessage.setMessage(vreq, lr.assembleWelcomeMessage());
String uri = lr.getRedirectionUriForLoggedInUser();
return new RedirectResponseValues(uri);
}
}

View file

@ -0,0 +1,64 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Template for creating an account for the first time an external user logs in. -->
<h3>First time log in</h3>
<#if errorEmailIsEmpty??>
<#assign errorMessage = "You must supply an email address." />
</#if>
<#if errorEmailInUse??>
<#assign errorMessage = "An account with that email address already exists." />
</#if>
<#if errorEmailInvalidFormat??>
<#assign errorMessage = "'${emailAddress}' is not a valid email address." />
</#if>
<#if errorFirstNameIsEmpty??>
<#assign errorMessage = "You must supply a first name." />
</#if>
<#if errorLastNameIsEmpty??>
<#assign errorMessage = "You must supply a last name." />
</#if>
<#if errorMessage?has_content>
<section id="error-alert" role="alert">
<img src="${urls.images}/iconAlert.png" width="24" height="24" alert="Error alert icon"/>
<p>${errorMessage}</p>
</section>
</#if>
<section id="first-time-login" role="region">
<fieldset>
<p class="note">
Please provide your contact information to finish creating your account.
</p>
<form method="POST" action="${formUrls.firstTimeExternal}" class="customForm" role="my account">
<input type="hidden" name="externalAuthId" value="${externalAuthId}" />
<label for="first-name">First name<span class="requiredHint"> *</span></label>
<input type="text" name="firstName" value="${firstName}" id="first-name" role="input "/>
<label for="last-name">Last name<span class="requiredHint"> *</span></label>
<input type="text" name="lastName" value="${lastName}" id="last-name" role="input "/>
<label for="email-address">Email address<span class="requiredHint"> *</span></label>
<input type="text" name="emailAddress" value="${emailAddress}" id="email-address" role="input "/>
<#if emailIsEnabled??>
<p class="note">
Note: An email will be sent to the address entered above notifying
that an account has been created.
</p>
</#if>
<input type="submit" name="submit" value="Create account" class="submit"/> or <a href="${urls.home}">Cancel</a>
</form>
</fieldset>
</section>
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')}

View file

@ -0,0 +1,26 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that an account has been created for an externally-authenticated user. -->
<html>
<head>
<title>${subjectLine}</title>
</head>
<body>
<p>
${userAccount.firstName} ${userAccount.lastName}
</p>
<p>
<strong>Congratulations!</strong>
</p>
<p>
We have created your new VIVO account associated with ${userAccount.emailAddress}.
</p>
<p>
Thanks!
</p>
</body>
</html>

View file

@ -0,0 +1,12 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that an account has been created for an externally-authenticated user. -->
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
Thanks!