/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.http.servlet;
import password.pwm.PwmApplication;
import password.pwm.PwmConstants;
import password.pwm.bean.EmailItemBean;
import password.pwm.bean.LocalSessionStateBean;
import password.pwm.bean.SessionLabel;
import password.pwm.bean.SmsItemBean;
import password.pwm.bean.UserIdentity;
import password.pwm.bean.UserInfoBean;
import password.pwm.config.Configuration;
import password.pwm.config.FormConfiguration;
import password.pwm.config.FormUtility;
import password.pwm.config.PwmSetting;
import password.pwm.config.option.MessageSendMethod;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.http.HttpMethod;
import password.pwm.http.JspUrl;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmRequestAttribute;
import password.pwm.http.PwmSession;
import password.pwm.ldap.LdapUserDataReader;
import password.pwm.ldap.search.SearchConfiguration;
import password.pwm.ldap.UserDataReader;
import password.pwm.ldap.search.UserSearchEngine;
import password.pwm.ldap.UserStatusReader;
import password.pwm.svc.stats.Statistic;
import password.pwm.util.CaptchaUtility;
import password.pwm.util.java.JavaHelper;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.macro.MacroMachine;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@WebServlet(
name="ForgottenUsernameServlet",
urlPatterns = {
PwmConstants.URL_PREFIX_PUBLIC + "/forgottenusername",
PwmConstants.URL_PREFIX_PUBLIC + "/ForgottenUsername",
}
)
public class ForgottenUsernameServlet extends AbstractPwmServlet {
private static final PwmLogger LOGGER = PwmLogger.forClass(ForgottenUsernameServlet.class);
public enum ForgottenUsernameAction implements AbstractPwmServlet.ProcessAction {
search,
;
public Collection<HttpMethod> permittedMethods()
{
return Collections.singletonList(HttpMethod.POST);
}
}
protected ForgottenUsernameAction readProcessAction(final PwmRequest request)
throws PwmUnrecoverableException
{
try {
return ForgottenUsernameAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
} catch (IllegalArgumentException e) {
return null;
}
}
protected void processAction(final PwmRequest pwmRequest)
throws ServletException, IOException, PwmUnrecoverableException
{
final Configuration config = pwmRequest.getConfig();
if (!config.readSettingAsBoolean(PwmSetting.FORGOTTEN_USERNAME_ENABLE)) {
pwmRequest.respondWithError(PwmError.ERROR_SERVICE_NOT_AVAILABLE.toInfo());
return;
}
final ForgottenUsernameAction action = readProcessAction(pwmRequest);
if (action != null) {
pwmRequest.validatePwmFormID();
switch (action) {
case search:
handleSearchRequest(pwmRequest);
return;
default:
JavaHelper.unhandledSwitchStatement(action);
}
}
forwardToFormJsp(pwmRequest);
}
public void handleSearchRequest(
final PwmRequest pwmRequest
)
throws PwmUnrecoverableException, IOException, ServletException
{
final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
final PwmSession pwmSession = pwmRequest.getPwmSession();
final LocalSessionStateBean ssBean = pwmSession.getSessionStateBean();
if (!CaptchaUtility.verifyReCaptcha(pwmRequest)) {
final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_BAD_CAPTCHA_RESPONSE);
LOGGER.debug(pwmRequest, errorInfo);
setLastError(pwmRequest, errorInfo);
forwardToFormJsp(pwmRequest);
return;
}
final String contextParam = pwmRequest.readParameterAsString(PwmConstants.PARAM_CONTEXT);
final String ldapProfile = pwmRequest.readParameterAsString(PwmConstants.PARAM_LDAP_PROFILE);
final List<FormConfiguration> forgottenUsernameForm = pwmApplication.getConfig().readSettingAsForm(PwmSetting.FORGOTTEN_USERNAME_FORM);
//read the values from the request
Map<FormConfiguration, String> formValues = new HashMap<>();
try {
formValues = FormUtility.readFormValuesFromRequest(pwmRequest,
forgottenUsernameForm, ssBean.getLocale());
// check for intruder search
pwmApplication.getIntruderManager().convenience().checkAttributes(formValues);
// see if the values meet the configured form requirements.
FormUtility.validateFormValues(pwmRequest.getConfig(), formValues, ssBean.getLocale());
final String searchFilter;
{
final String configuredSearchFilter = pwmApplication.getConfig().readSettingAsString(PwmSetting.FORGOTTEN_USERNAME_SEARCH_FILTER);
if (configuredSearchFilter == null || configuredSearchFilter.isEmpty()) {
searchFilter = FormUtility.ldapSearchFilterForForm(pwmApplication, forgottenUsernameForm);
LOGGER.trace(pwmSession,"auto generated ldap search filter: " + searchFilter);
} else {
searchFilter = configuredSearchFilter;
}
}
final UserIdentity userIdentity;
{
final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
final SearchConfiguration searchConfiguration = SearchConfiguration.builder()
.filter(searchFilter)
.formValues(formValues)
.ldapProfile(ldapProfile)
.contexts(Collections.singletonList(contextParam))
.build();
userIdentity = userSearchEngine.performSingleUserSearch(searchConfiguration, pwmSession.getLabel());
}
if (userIdentity == null) {
pwmApplication.getIntruderManager().convenience().markAddressAndSession(pwmSession);
pwmApplication.getStatisticsManager().incrementValue(Statistic.FORGOTTEN_USERNAME_FAILURES);
setLastError(pwmRequest, PwmError.ERROR_CANT_MATCH_USER.toInfo());
forwardToFormJsp(pwmRequest);
return;
}
// make sure the user isn't locked.
pwmApplication.getIntruderManager().convenience().checkUserIdentity(userIdentity);
final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, pwmSession.getLabel());
final UserInfoBean forgottenUserInfo = userStatusReader.populateUserInfoBean(pwmRequest.getLocale(), userIdentity);
// send username
sendUsername(pwmApplication, pwmSession, forgottenUserInfo);
pwmApplication.getIntruderManager().convenience().clearAddressAndSession(pwmSession);
pwmApplication.getIntruderManager().convenience().clearAttributes(formValues);
pwmApplication.getStatisticsManager().incrementValue(Statistic.FORGOTTEN_USERNAME_SUCCESSES);
// redirect user to success page.
forwardToCompletePage(pwmRequest, userIdentity);
return;
} catch (PwmOperationalException e) {
final ErrorInformation errorInfo;
errorInfo = e.getError() == PwmError.ERROR_UNKNOWN
? new ErrorInformation(PwmError.ERROR_CANT_MATCH_USER,e.getErrorInformation().getDetailedErrorMsg(),
e.getErrorInformation().getFieldValues())
: e.getErrorInformation();
setLastError(pwmRequest, errorInfo);
pwmApplication.getIntruderManager().convenience().markAddressAndSession(pwmSession);
pwmApplication.getIntruderManager().convenience().markAttributes(formValues, pwmSession);
}
pwmApplication.getStatisticsManager().incrementValue(Statistic.FORGOTTEN_USERNAME_FAILURES);
forwardToFormJsp(pwmRequest);
}
private void sendUsername(
final PwmApplication pwmApplication,
final PwmSession pwmSession,
final UserInfoBean forgottenUserInfo
)
throws PwmOperationalException, PwmUnrecoverableException
{
final Locale userLocale = pwmSession.getSessionStateBean().getLocale();
final Configuration configuration = pwmApplication.getConfig();
final MessageSendMethod messageSendMethod = configuration.readSettingAsEnum(PwmSetting.FORGOTTEN_USERNAME_SEND_USERNAME_METHOD, MessageSendMethod.class);
final EmailItemBean emailItemBean = configuration.readSettingAsEmail(PwmSetting.EMAIL_SEND_USERNAME, userLocale);
final String smsMessage = configuration.readSettingAsLocalizedString(PwmSetting.SMS_FORGOTTEN_USERNAME_TEXT, userLocale);
if (messageSendMethod == null || messageSendMethod == MessageSendMethod.NONE) {
return;
}
sendMessageViaMethod(
pwmApplication,
pwmSession.getLabel(),
forgottenUserInfo,
messageSendMethod,
emailItemBean,
smsMessage
);
}
private static void sendMessageViaMethod(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final UserInfoBean userInfoBean,
final MessageSendMethod messageSendMethod,
final EmailItemBean emailItemBean,
final String smsMessage
)
throws PwmOperationalException, PwmUnrecoverableException
{
if (pwmApplication == null) {
throw new IllegalArgumentException("pwmApplication can not be null");
}
if (userInfoBean == null) {
throw new IllegalArgumentException("userInfoBean can not be null");
}
ErrorInformation error = null;
switch (messageSendMethod) {
case NONE:
break;
case BOTH:
// Send both email and SMS, success if one of both succeeds
final ErrorInformation err1 = sendEmailViaMethod(pwmApplication, sessionLabel, userInfoBean, emailItemBean);
final ErrorInformation err2 = sendSmsViaMethod(pwmApplication, sessionLabel, userInfoBean, smsMessage);
if (err1 != null) {
error = err1;
} else if (err2 != null) {
error = err2;
}
break;
case EMAILFIRST:
// Send email first, try SMS if email is not available
error = sendEmailViaMethod(pwmApplication, sessionLabel, userInfoBean, emailItemBean);
if (error != null) {
error = sendSmsViaMethod(pwmApplication, sessionLabel, userInfoBean, smsMessage);
}
break;
case SMSFIRST:
// Send SMS first, try email if SMS is not available
error = sendSmsViaMethod(pwmApplication, sessionLabel, userInfoBean, smsMessage);
if (error != null) {
error = sendEmailViaMethod(pwmApplication, sessionLabel, userInfoBean, emailItemBean);
}
break;
case SMSONLY:
// Only try SMS
error = sendSmsViaMethod(pwmApplication, sessionLabel, userInfoBean, smsMessage);
break;
case EMAILONLY:
default:
// Only try email
error = sendEmailViaMethod(pwmApplication, sessionLabel, userInfoBean, emailItemBean);
break;
}
if (error != null) {
throw new PwmOperationalException(error);
}
}
private static ErrorInformation sendSmsViaMethod(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final UserInfoBean userInfoBean,
final String smsMessage
)
throws PwmOperationalException, PwmUnrecoverableException
{
final String toNumber = userInfoBean.getUserSmsNumber();
if (toNumber == null || toNumber.length() < 1) {
final String errorMsg = String.format("unable to send new password email for '%s'; no SMS number available in ldap", userInfoBean.getUserIdentity());
return new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
}
final UserDataReader userDataReader = LdapUserDataReader.appProxiedReader(pwmApplication, userInfoBean.getUserIdentity());
final MacroMachine macroMachine = new MacroMachine(pwmApplication, sessionLabel, userInfoBean, null, userDataReader);
final SmsItemBean smsItem = new SmsItemBean(toNumber, smsMessage);
pwmApplication.sendSmsUsingQueue(smsItem, macroMachine);
return null;
}
private static ErrorInformation sendEmailViaMethod(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final UserInfoBean userInfoBean,
final EmailItemBean emailItemBean
)
throws PwmUnrecoverableException
{
if (emailItemBean == null) {
final String errorMsg = "emailItemBean is null";
return new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
}
final UserDataReader userDataReader = LdapUserDataReader.appProxiedReader(pwmApplication, userInfoBean.getUserIdentity());
final MacroMachine macroMachine = new MacroMachine(pwmApplication, sessionLabel, userInfoBean, null, userDataReader);
pwmApplication.getEmailQueue().submitEmail(emailItemBean, userInfoBean, macroMachine);
return null;
}
private void forwardToFormJsp(final PwmRequest pwmRequest)
throws ServletException, PwmUnrecoverableException, IOException
{
pwmRequest.addFormInfoToRequestAttr(PwmSetting.FORGOTTEN_USERNAME_FORM,false,false);
pwmRequest.forwardToJsp(JspUrl.FORGOTTEN_USERNAME);
}
private static void forwardToCompletePage(final PwmRequest pwmRequest, final UserIdentity userIdentity)
throws PwmUnrecoverableException, ServletException, IOException
{
final Locale locale = pwmRequest.getLocale();
final String completeMessage = pwmRequest.getConfig().readSettingAsLocalizedString(PwmSetting.FORGOTTEN_USERNAME_MESSAGE,locale);
final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
final String expandedText = macroMachine.expandMacros(completeMessage);
pwmRequest.setAttribute(PwmRequestAttribute.CompleteText, expandedText);
pwmRequest.forwardToJsp(JspUrl.FORGOTTEN_USERNAME_COMPLETE);
}
}