Skip to content

Commit

Permalink
Merge pull request #421 from ivanmrsulja/feature/forgot-password
Browse files Browse the repository at this point in the history
Feature/forgot password
  • Loading branch information
chenejac committed May 10, 2024
2 parents 012c9de + 44f9bdd commit 2abe323
Show file tree
Hide file tree
Showing 22 changed files with 1,391 additions and 376 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javax.servlet.annotation.WebServlet;

import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.beans.CaptchaBundle;
import edu.cornell.mannlib.vitro.webapp.beans.CaptchaServiceBean;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package edu.cornell.mannlib.vitro.webapp.web.widgets;

import java.util.Map;
import java.util.Objects;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
Expand All @@ -25,58 +26,16 @@
import freemarker.template.utility.DeepUnwrap;

public class LoginWidget extends Widget {
private static final Log log = LogFactory.getLog(LoginWidget.class);
private static final Log log = LogFactory.getLog(LoginWidget.class);

/** The page that kicks off the External Authentication process. */
private static final String EXTERNAL_AUTH_SETUP_URL = "/loginExternalAuth";

private static enum Macro {
LOGIN("loginForm"),
FORCE_PASSWORD_CHANGE("forcePasswordChange"),
ALREADY_LOGGED_IN("alreadyLoggedIn"),
SERVER_ERROR("error");

private final String macroName;

Macro(String macroName) {
this.macroName = macroName;
}

@Override
public String toString() {
return macroName;
}

}

private static enum TemplateVariable {
LOGIN_NAME("loginName"),
FORM_ACTION("formAction"),
INFO_MESSAGE("infoMessage"),
ERROR_MESSAGE("errorMessage"),
EXTERNAL_AUTH_NAME("externalAuthName"),
EXTERNAL_AUTH_URL("externalAuthUrl"),
CANCEL_URL("cancelUrl"),
SITE_NAME("siteName"),
MINIMUM_PASSWORD_LENGTH("minimumPasswordLength"),
MAXIMUM_PASSWORD_LENGTH("maximumPasswordLength");

private final String variableName;

TemplateVariable(String variableName) {
this.variableName = variableName;
}

@Override
public String toString() {
return variableName;
}

}
/**
* The page that kicks off the External Authentication process.
*/
private static final String EXTERNAL_AUTH_SETUP_URL = "/loginExternalAuth";

@Override
protected WidgetTemplateValues process(Environment env, Map params,
HttpServletRequest request, ServletContext context) {
HttpServletRequest request, ServletContext context) {

WidgetTemplateValues values = null;

Expand All @@ -88,29 +47,29 @@ protected WidgetTemplateValues process(Environment env, Map params,
TemplateHashModel dataModel = env.getDataModel();

switch (state) {
case LOGGED_IN:
// On the login page itself, show a message that the user is already logged in.
// Otherwise, when redirecting to login page from a page that the logged-in user
// doesn't have access to, we would just show a blank page.
if (request.getServletPath().equals(Route.LOGIN.path())) {
values = showMessageToLoggedInUser(request);
case LOGGED_IN:
// On the login page itself, show a message that the user is already logged in.
// Otherwise, when redirecting to login page from a page that the logged-in user
// doesn't have access to, we would just show a blank page.
if (request.getServletPath().equals(Route.LOGIN.path())) {
values = showMessageToLoggedInUser(request);
break;
} else {
return null;
}
case FORCED_PASSWORD_CHANGE:
values = showPasswordChangeScreen(request);
break;
} else {
return null;
}
case FORCED_PASSWORD_CHANGE:
values = showPasswordChangeScreen(request);
break;
default:
values = showLoginScreen(request, dataModel.get("siteName").toString());
default:
values = showLoginScreen(request, dataModel.get("siteName").toString());
}

values.put("urls", dataModel.get("urls"));
values.put("currentServlet", dataModel.get("currentServlet"));

@SuppressWarnings("unchecked")
Map<String, Object> dm = (Map<String, Object>) DeepUnwrap.permissiveUnwrap(dataModel);
User user = (User) dm.get("user");
User user = (User) dm.get("user");
values.put("user", user);

} catch (Exception e) {
Expand All @@ -131,17 +90,27 @@ private WidgetTemplateValues showLoginScreen(HttpServletRequest request, String
LoginProcessBean bean = LoginProcessBean.getBean(request);
log.trace("Going to login screen: " + bean);

String forgotPasswordEnabled = ConfigurationProperties.getInstance()
.getProperty("authentication.forgotPassword");

if (forgotPasswordEnabled == null) {
forgotPasswordEnabled = "disabled";
}

WidgetTemplateValues values = new WidgetTemplateValues(Macro.LOGIN.toString());
values.put(TemplateVariable.FORM_ACTION.toString(), getAuthenticateUrl(request));
values.put(TemplateVariable.LOGIN_NAME.toString(), bean.getUsername());

boolean showExternalAuth = StringUtils.isNotBlank(
ConfigurationProperties.getBean(request).getProperty(
"externalAuth.netIdHeaderName"));
if (showExternalAuth) {
values.put(TemplateVariable.EXTERNAL_AUTH_URL.toString(),
UrlBuilder.getUrl(EXTERNAL_AUTH_SETUP_URL));
}
values.put(TemplateVariable.FORGOT_PASSWORD.toString(), getForgotPasswordUrl(request));
values.put(TemplateVariable.FORGOT_PASSWORD_ENABLED.toString(),
forgotPasswordEnabled.equalsIgnoreCase("enabled"));

boolean showExternalAuth = StringUtils.isNotBlank(
ConfigurationProperties.getInstance().getProperty(
"externalAuth.netIdHeaderName"));
if (showExternalAuth) {
values.put(TemplateVariable.EXTERNAL_AUTH_URL.toString(),
UrlBuilder.getUrl(EXTERNAL_AUTH_SETUP_URL));
}

String infoMessage = bean.getInfoMessageAndClear();
if (!infoMessage.isEmpty()) {
Expand Down Expand Up @@ -170,7 +139,7 @@ private WidgetTemplateValues showPasswordChangeScreen(HttpServletRequest request
log.trace("Going to password change screen: " + bean);

WidgetTemplateValues values = new WidgetTemplateValues(
Macro.FORCE_PASSWORD_CHANGE.toString());
Macro.FORCE_PASSWORD_CHANGE.toString());
values.put(TemplateVariable.FORM_ACTION.toString(), getAuthenticateUrl(request));
values.put(TemplateVariable.CANCEL_URL.toString(), getCancelUrl(request));
values.put(TemplateVariable.MINIMUM_PASSWORD_LENGTH.toString(), UserAccount.MIN_PASSWORD_LENGTH);
Expand All @@ -185,7 +154,7 @@ private WidgetTemplateValues showPasswordChangeScreen(HttpServletRequest request

private WidgetTemplateValues showError(Exception e) {
WidgetTemplateValues values = new WidgetTemplateValues(
Macro.SERVER_ERROR.toString());
Macro.SERVER_ERROR.toString());
values.put(TemplateVariable.ERROR_MESSAGE.toString(), "Internal server error:<br /> " + e);
return values;
}
Expand All @@ -198,38 +167,95 @@ private State getCurrentLoginState(HttpServletRequest request) {
return State.LOGGED_IN;
}
if (isOutdatedLoginProcessBean(request)) {
LoginProcessBean.removeBean(request);
LoginProcessBean.removeBean(request);
}
return LoginProcessBean.getBean(request).getState();
}

/**
* A LoginProcessBean is outdated unless the the "in-process" flag is set in the
* session.
*
* Each time we hit Authenticate, the flag is set, and each time
* we draw the widget it is reset.
*/
private boolean isOutdatedLoginProcessBean(HttpServletRequest request) {
boolean inProcess = LoginInProcessFlag.checkAndReset(request);
if (!inProcess) {
log.debug("The process bean is outdated. Discard it.");
}

return !inProcess;
}

/** What's the URL for this servlet? */
/**
* A LoginProcessBean is outdated unless the the "in-process" flag is set in the
* session.
* Each time we hit Authenticate, the flag is set, and each time
* we draw the widget it is reset.
*/
private boolean isOutdatedLoginProcessBean(HttpServletRequest request) {
boolean inProcess = LoginInProcessFlag.checkAndReset(request);
if (!inProcess) {
log.debug("The process bean is outdated. Discard it.");
}

return !inProcess;
}

/**
* What's the URL for this servlet?
*/
private String getAuthenticateUrl(HttpServletRequest request) {
String contextPath = request.getContextPath();
return contextPath + "/authenticate";
}

/** What's the URL for this servlet, with the cancel parameter added? */
/**
* What's the URL for this servlet, with the cancel parameter added?
*/
private String getCancelUrl(HttpServletRequest request) {
String contextPath = request.getContextPath();
String urlParams = "?cancel=true";
return contextPath + "/authenticate" + urlParams;
}

/**
* What's the password recovery URL for this servlet?
*/
private String getForgotPasswordUrl(HttpServletRequest request) {
String contextPath = request.getContextPath();
return contextPath + "/forgotPassword";
}

private enum Macro {
LOGIN("loginForm"),
FORCE_PASSWORD_CHANGE("forcePasswordChange"),
ALREADY_LOGGED_IN("alreadyLoggedIn"),
SERVER_ERROR("error");

private final String macroName;

Macro(String macroName) {
this.macroName = macroName;
}

@Override
public String toString() {
return macroName;
}

}

private enum TemplateVariable {
LOGIN_NAME("loginName"),
FORM_ACTION("formAction"),
FORGOT_PASSWORD("forgotPassword"),
FORGOT_PASSWORD_ENABLED("forgotPasswordEnabled"),
INFO_MESSAGE("infoMessage"),
ERROR_MESSAGE("errorMessage"),
EXTERNAL_AUTH_NAME("externalAuthName"),
EXTERNAL_AUTH_URL("externalAuthUrl"),
CANCEL_URL("cancelUrl"),
SITE_NAME("siteName"),
MINIMUM_PASSWORD_LENGTH("minimumPasswordLength"),
MAXIMUM_PASSWORD_LENGTH("maximumPasswordLength");

private final String variableName;

TemplateVariable(String variableName) {
this.variableName = variableName;
}

@Override
public String toString() {
return variableName;
}

}

}
4 changes: 4 additions & 0 deletions home/src/main/resources/config/example.runtime.properties
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing
#comma separated list of mime types allowed for upload
#fileUpload.allowedMIMETypes = image/png, application/pdf

# Feature toggle for forgot password functionality [enabled, disabled]
authentication.forgotPassword = disabled
authentication.forgotPassword.notify-admin = false

# Captcha configuration. Available implementations are: nanocaptcha (text-based) and recaptchav2
# nanocaptcha is available in 2 difficulties (easy and hard)
# If captcha.implementation property is not provided, system will fall back to nanocaptcha implementation
Expand Down
Loading

0 comments on commit 2abe323

Please sign in to comment.