Skip to content

Commit

Permalink
Preliminary api test for email confirmation IQSS#2170
Browse files Browse the repository at this point in the history
  • Loading branch information
bsilverstein95 committed Jul 8, 2016
1 parent e29cda9 commit 2d6ba01
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public enum ActionType {

Admin,

GlobalGroups
GlobalGroups, AuthenticatedUser
}

@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
*/
public abstract class AbstractApiBean {

private static final Logger logger = Logger.getLogger(AbstractApiBean.class.getName());
static final Logger logger = Logger.getLogger(AbstractApiBean.class.getName());
private static final String DATAVERSE_KEY_HEADER_NAME = "X-Dataverse-key";

/**
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Admin.java
Original file line number Diff line number Diff line change
Expand Up @@ -520,5 +520,14 @@ public Response validate() {
}
return okResponse(msg);
}

@Path("confirmEmail/{token}")
@GET
public Response getConfirmEmailToken(@PathParam("token") String token) {


return null;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,26 @@ public class AuthenticatedUser implements User, Serializable {
private String position;
private String lastName;
private String firstName;
private String confirmToken;
@Column(nullable = true)
private Timestamp emailConfirmed;

public Timestamp getEmailConfirmed() {
return emailConfirmed;
}

public void setEmailConfirmed(Timestamp emailConfirmed) {
this.emailConfirmed = emailConfirmed;
}
private boolean superuser;

public String getConfirmToken() {
return confirmToken;
}

public void setConfirmToken(String confirmToken){
this.confirmToken = confirmToken;
}
/**
* @todo Remove? Check for accuracy? For Solr JOINs we used to care about
* the modification times of users but now we don't index users at all.
Expand Down Expand Up @@ -118,7 +136,7 @@ public void applyDisplayInfo( AuthenticatedUserDisplayInfo inf ) {
setEmail(inf.getEmailAddress());
setAffiliation( inf.getAffiliation() );
setPosition( inf.getPosition());

}

@Override
Expand Down Expand Up @@ -242,4 +260,6 @@ public void setShibIdentityProvider(String shibIdentityProvider) {
this.shibIdentityProvider = shibIdentityProvider;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import javax.persistence.Table;

/**
* @todo Confirm proper transition from builtinuser to authenticateduser
* before removing the import
*
* @author bsilverstein
* @todo: Make the feature restrict a user until they are confirmed
*/

@Table(indexes = {@Index(columnList="token")
Expand Down Expand Up @@ -55,22 +55,21 @@ public class ConfirmEmailData implements Serializable{
@Column(nullable = false)
private Timestamp expires;

public ConfirmEmailData() {
}


public ConfirmEmailData(AuthenticatedUser anAuthenticatedUser) {
authenticatedUser = anAuthenticatedUser;
token = UUID.randomUUID().toString();
long nowInMilliseconds = new Date().getTime();
created = new Timestamp(nowInMilliseconds);
long ONE_MINUTE_IN_MILLISECONDS = 60000;
/** @todo: not utilize getMinutesUntilPasswordResetTokenExpires --
* maybe consolidate the method in SystemConfig to just be about tokens
* rather than duplicate and re-brand the same method
* @todo: make the token's time before expiration way longer
/**
* @todo: make the token's time before expiration way longer
*
* @todo: use database setting instead of jvm option for line 75 configurable expiration value
*/

long futureInMilliseconds = nowInMilliseconds + (SystemConfig.getMinutesUntilPasswordResetTokenExpires() * ONE_MINUTE_IN_MILLISECONDS);
long futureInMilliseconds = nowInMilliseconds + (SystemConfig.getMinutesUntilConfirmEmailTokenExpires() * ONE_MINUTE_IN_MILLISECONDS);
expires = new Timestamp(new Date(futureInMilliseconds).getTime());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void init() {
if (confirmEmailData != null) {
user = confirmEmailData.getAuthenticatedUser();
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Password Reset Link", "Your password reset link is not valid."));
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Confirm Email Link", "Your email confirmation link is not valid."));
}
}
}
Expand All @@ -100,31 +100,33 @@ public String sendEmailConfirmLink() {
} else {
logger.log(Level.INFO, "Couldn''t find single account using {0}", emailAddress);
}
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Password Reset Initiated", ""));
} catch (ConfirmEmailException ex) {
/**
* @todo do we really need a special exception for this??
*/
logger.log(Level.WARNING, "Error While resetting password: " + ex.getMessage(), ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Email Confirmation Initiated", ""));
} catch (ConfirmEmailException ex) {
logger.log(Level.WARNING, "Error While confirming email: " + ex.getMessage(), ex);
}
return "";
}
//Huge mess below! Need to figure out what's up with this
// public String confirmEmail() {
// public String confirmEmail() throws ConfirmEmailException {
// ConfirmEmailInitResponse response;
// response = confirmEmailService.sendConfirm(user, false);
// if (response.isConfirmed()) {
// try { if (response.isEmailFound()) {
// FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, response.getMessageSummary(), response.getMessageDetail()));
// String authProviderId = AuthenticationProvider.PROVIDER_ID;
// AuthenticatedUser au = authSvc.lookupUser(builtinAuthProviderId, user.getUserName());
// AuthenticatedUser au = authSvc.lookupUser(builtinAuthProviderId, user.getUserIdentifier());
// session.setUser(au);
// return "/dataverse.xhtml?alias=" + dataverseService.findRootDataverse().getAlias() + "faces-redirect=true";
// } else {
// FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, response.getMessageSummary(), response.getMessageDetail()));
// return null;
// }
// }
//
// } catch(Exception ex) {
// String msg = "Unable to save token for " + user.getEmail();
// throw new ConfirmEmailException(msg, ex);
// }
// }

public String getToken() {
return token;
}
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,15 @@ public String getSolrHostColonPort() {
}



/**
* The number of minutes for which a confirmation email link is valid.
* @todo: ask Phil about making it possible to set this value as sysadmin
*
*/
public static int getMinutesUntilConfirmEmailTokenExpires() {
final int reasonableDefault = 60;
return reasonableDefault;
}
/**
* The number of minutes for which a password reset token is valid. Can be
* overridden by {@link #PASSWORD_RESET_TIMEOUT_IN_MINUTES}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public static JsonObjectBuilder jsonForAuthUser(AuthenticatedUser authenticatedU
.add("affiliation", authenticatedUser.getAffiliation())
.add("position", authenticatedUser.getPosition())
.add("persistentUserId", authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId())
.add("confirmToken", authenticatedUser.getConfirmToken())
.add("authenticationProviderId", authenticatedUser.getAuthenticatedUserLookup().getAuthenticationProviderId());
}

Expand Down
144 changes: 144 additions & 0 deletions src/main/webapp/subset/confirmemail.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:jsf="http://xmlns.jcp.org/jsf">

<h:head>
</h:head>

<h:body>
<ui:composition template="/dataverse_template.xhtml">
<ui:param name="pageTitle" value="#{bundle['pageTitle.confirmEmail.pre']} - #{dataverseServiceBean.findRootDataverse().name} #{bundle.dataverse}"/>
<ui:param name="dataverse" value="#{dataverseServiceBean.findRootDataverse()}"/>
<ui:param name="showDataverseHeader" value="false"/>
<ui:param name="loginRedirectPage" value="dataverse.xhtml"/>
<ui:define name="body">
<f:metadata>
<f:viewParam name="token" value="#{ConfirmEmailPage.token}"/>
<f:viewAction action="#{ConfirmEmailPage.init}" />
</f:metadata>

<ui:fragment id="debugVariables" rendered="false">
<div style="background-color: lightgray; margin-top:3em;">
<tt>
<h:outputText value="#{bundle['confirmEmail.token']} #{ConfirmEmailPage.token}"/><br/>
<h:outputText value="#{bundle['confirmEmail.userLookedUp']} #{ConfirmEmailPage.user.userName}"/><br/>
<h:outputText value="#{bundle['confirmEmail.emailSubmitted']} #{ConfirmEmailPage.emailAddress}"/><br/>
</tt>
</div>
</ui:fragment>

<ui:fragment rendered="#{empty ConfirmEmailPage.token}">
<div jsf:rendered="#{empty ConfirmEmailPage.emailAddress}">
<div class="alert alert-info">
<span class="glyphicon glyphicon-info-sign"></span>
<h:outputFormat value="#{bundle['confirmEmail.details']}" escape="false">
<f:param value="&lt;strong&gt;"/>
<f:param value="&lt;/strong&gt;"/>
</h:outputFormat>
</div>
<h:form id="confirmEmail" styleClass="form-horizontal">
<div class="form-group col-sm-11">
<p:outputLabel value="#{bundle.email}" for="email" styleClass="col-sm-3 control-label"/>
<div class="col-sm-4">
<p:inputText id="email" value="#{ConfirmEmailPage.emailAddress}" styleClass="form-control"/>
<p:watermark for="email" value="[email protected]"/>
<p:focus context="confirmEmail"/>
<p:message for="email" display="text" />
</div>
</div>
<div class="form-group col-sm-11">
<div class="col-sm-offset-3 col-sm-9 button-block">
<p:commandButton action="#{ConfirmEmailPage.sendEmailConfirmLink()}" update="@all" value="#{bundle['confirmEmail.submitRequest']}"/>
<p:button value="#{bundle.cancel}" outcome="/loginpage.xhtml"/>
</div>
</div>
</h:form>
</div>
<ui:fragment rendered="#{not empty ConfirmEmailPage.emailAddress}">
<h:outputFormat value="#{bundle['confirmEmail.successSubmit.tip']}">
<f:param value="#{ConfirmEmailPage.emailAddress}"/>
</h:outputFormat>
<ui:fragment id="debugEmail" rendered="false">
<div style="background-color: lightgray">
<tt>
<h:outputText value="#{bundle['confirmEmail.debug']}"/><br/>
<ui:fragment rendered="#{not empty ConfirmEmailPage.confirmEmailUrl}">
<h:outputText value="#{bundle['confirmEmail.resetUrl']} "/>
<h:outputLink value="#{ConfirmEmailPage.confirmEmailUrl}">
<h:outputText value="#{ConfirmEmailPage.confirmEmailUrl}"/>
</h:outputLink>
</ui:fragment>
<ui:fragment rendered="#{empty ConfirmEmailPage.passwordResetUrl}">
<h:outputFormat value="#{bundle['confirmEmail.noEmail.tip']}">
<f:param value="#{ConfirmEmailPage.emailAddress}"/>
</h:outputFormat>
</ui:fragment>
</tt>
</div>
</ui:fragment>
</ui:fragment>
</ui:fragment>

<ui:fragment rendered="#{not empty ConfirmEmailPage.token}">
<h:outputFormat value="#{bundle['confirmEmail.illegalLink.tip']}" escape="false" rendered="#{empty ConfirmEmailPage.user}">
<f:param value="&lt;a href='/confirmemail.xhtml'&gt;"/>
<f:param value="&lt;/a&gt;"/>
</h:outputFormat>
<ui:fragment rendered="#{not empty ConfirmEmailPage.user}">
<div class="alert alert-info">
<span class="glyphicon glyphicon-info-sign"></span>
<!-- <h:outputFormat value="#{bundle['passwdReset.newPasswd.details']}" escape="false">
<f:param value="&lt;strong&gt;"/>
<f:param value="&lt;/strong&gt;"/>
</h:outputFormat>-->
</div>
<!-- <div class="row" jsf:rendered="#{ConfirmEmailPage.isAccountUpgrade()}">
<div class="col-sm-12">
<h:outputFormat styleClass="h2 text-info show" value="#{bundle['user.updatePassword.welcome']}">
<f:param value="#{systemConfig.getVersion(true)}"/>
<f:param value="#{PasswordResetPage.user.displayInfo.title}"/>
</h:outputFormat>
<p class="help-block"><span class="text-danger"><span class="glyphicon glyphicon-warning-sign"/> #{bundle['user.updatePassword.warning']}</span></p>
</div>
</div>-->
<!-- <div>
<h:form styleClass="form-horizontal">
<div class="form-group col-sm-11">
<label for="inputPassword" class="col-sm-3 control-label">
#{bundle['passwdReset.newPasswd']} <span class="glyphicon glyphicon-asterisk text-danger" title="#{bundle.requiredField}"></span>
</label>
<div class="col-sm-4">
<p:password id="inputPassword" label="#{bundle.passwd}" match="retypePassword" styleClass="form-control" value="#{PasswordResetPage.newPassword}"/>
<p:message for="inputPassword" />
</div>
</div>-->
<!-- <div class="form-group col-sm-11">
<label for="retypePassword" class="col-sm-3 control-label">
#{bundle['passwdReset.rePasswd']} <span class="glyphicon glyphicon-asterisk text-danger" title="#{bundle.requiredField}"></span>
</label>
<div class="col-sm-4">
<p:password id="retypePassword" label="#{bundle['passwdReset.rePasswd']}" styleClass="form-control" value="#{PasswordResetPage.newPassword}"/>
<p:message for="retypePassword" />
</div>
</div>
<ui:fragment rendered="#{PasswordResetPage.isAccountUpgrade()}">
Terms of Use
<ui:include src="termsofuse.xhtml"/>
</ui:fragment>
<div class="form-group col-sm-11">
<div class="col-sm-10 button-block">
<p:commandButton action="#{PasswordResetPage.resetPassword()}" update="@all" value="#{bundle['passwdReset.resetBtn']}"/>
</div>
</div>-->
</h:form>
</div>
</ui:fragment>
</ui:fragment>
</ui:define>
</ui:composition>
</h:body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void testLeadingWhitespaceInEmailAddress() {
// the backend will trim the email address
String emailExpected = email.trim();
assertEquals(emailExpected, emailActual);
}
}

@Test
public void testLeadingWhitespaceInUsername() {
Expand Down Expand Up @@ -113,7 +113,7 @@ public void testLogin() {
}

}

private Response createUser(String username, String firstName, String lastName, String email) {
String userAsJson = getUserAsJsonString(username, firstName, lastName, email);
String password = getPassword(userAsJson);
Expand Down
Loading

0 comments on commit 2d6ba01

Please sign in to comment.