/* * 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 com.novell.ldapchai.ChaiUser; import com.novell.ldapchai.exception.ChaiException; import com.novell.ldapchai.exception.ChaiUnavailableException; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.bean.EmailItemBean; import password.pwm.bean.TokenVerificationProgress; import password.pwm.bean.UserIdentity; import password.pwm.bean.UserInfoBean; import password.pwm.config.ActionConfiguration; 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.TokenStorageMethod; import password.pwm.config.profile.LdapProfile; import password.pwm.config.profile.UpdateAttributesProfile; import password.pwm.error.ErrorInformation; import password.pwm.error.PwmDataValidationException; import password.pwm.error.PwmError; import password.pwm.error.PwmException; import password.pwm.error.PwmOperationalException; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.HttpMethod; import password.pwm.http.JspUrl; import password.pwm.http.ProcessStatus; import password.pwm.http.PwmRequest; import password.pwm.http.PwmRequestAttribute; import password.pwm.http.PwmSession; import password.pwm.http.bean.UpdateProfileBean; import password.pwm.i18n.Message; import password.pwm.ldap.LdapOperationsHelper; import password.pwm.ldap.UserDataReader; import password.pwm.ldap.UserStatusReader; import password.pwm.svc.event.AuditEvent; import password.pwm.svc.event.AuditRecord; import password.pwm.svc.event.AuditRecordFactory; import password.pwm.svc.stats.Statistic; import password.pwm.svc.token.TokenPayload; import password.pwm.svc.token.TokenService; import password.pwm.svc.token.TokenType; import password.pwm.util.logging.PwmLogger; import password.pwm.util.macro.MacroMachine; import password.pwm.util.operations.ActionExecutor; import password.pwm.ws.server.RestResultBean; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; /** * User interaction servlet for updating user attributes * * @author Jason D. Rivard */ @WebServlet( name="UpdateProfileServlet", urlPatterns = { PwmConstants.URL_PREFIX_PRIVATE + "/updateprofile", PwmConstants.URL_PREFIX_PRIVATE + "/UpdateProfile" } ) public class UpdateProfileServlet extends ControlledPwmServlet { private static final PwmLogger LOGGER = PwmLogger.forClass(UpdateProfileServlet.class); public enum UpdateProfileAction implements AbstractPwmServlet.ProcessAction { updateProfile(HttpMethod.POST), agree(HttpMethod.POST), confirm(HttpMethod.POST), unConfirm(HttpMethod.POST), validate(HttpMethod.POST), enterCode(HttpMethod.POST), ; private final HttpMethod method; UpdateProfileAction(final HttpMethod method) { this.method = method; } public Collection<HttpMethod> permittedMethods() { return Collections.singletonList(method); } } @Override public Class<? extends ProcessAction> getProcessActionsClass() { return UpdateProfileAction.class; } private static UpdateAttributesProfile getProfile(final PwmRequest pwmRequest) { return pwmRequest.getPwmSession().getSessionManager().getUpdateAttributeProfile(pwmRequest.getPwmApplication()); } private static UpdateProfileBean getBean(final PwmRequest pwmRequest) throws PwmUnrecoverableException { return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, UpdateProfileBean.class); } @ActionHandler(action = "enterCode") ProcessStatus handleEnterCodeRequest( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException, ServletException, ChaiUnavailableException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final UpdateProfileBean updateProfileBean = getBean(pwmRequest); final PwmSession pwmSession = pwmRequest.getPwmSession(); final String userEnteredCode = pwmRequest.readParameterAsString(PwmConstants.PARAM_TOKEN); boolean tokenPassed = false; ErrorInformation errorInformation = null; try { final TokenPayload tokenPayload = pwmApplication.getTokenService().processUserEnteredCode( pwmSession, pwmRequest.getUserInfoIfLoggedIn(), null, userEnteredCode ); if (tokenPayload != null) { if (TokenType.UPDATE_EMAIL.matchesName(tokenPayload.getName())) { LOGGER.debug(pwmRequest, "email token passed"); updateProfileBean.getTokenVerificationProgress().getPassedTokens().add(TokenVerificationProgress.TokenChannel.EMAIL); updateProfileBean.getTokenVerificationProgress().getIssuedTokens().add(TokenVerificationProgress.TokenChannel.EMAIL); updateProfileBean.getTokenVerificationProgress().setPhase(null); tokenPassed = true; } else if (TokenType.UPDATE_SMS.matchesName(tokenPayload.getName())) { LOGGER.debug(pwmRequest, "SMS token passed"); updateProfileBean.getTokenVerificationProgress().getPassedTokens().add(TokenVerificationProgress.TokenChannel.SMS); updateProfileBean.getTokenVerificationProgress().getIssuedTokens().add(TokenVerificationProgress.TokenChannel.SMS); updateProfileBean.getTokenVerificationProgress().setPhase(null); tokenPassed = true; } else { final String errorMsg = "token name/type is not recognized: " + tokenPayload.getName(); errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT,errorMsg); } } } catch (PwmOperationalException e) { final String errorMsg = "token incorrect: " + e.getMessage(); errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT, errorMsg); } if (!tokenPassed) { if (errorInformation == null) { errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT); } LOGGER.debug(pwmSession, errorInformation.toDebugStr()); setLastError(pwmRequest, errorInformation); } return ProcessStatus.Continue; } @ActionHandler(action = "validate") ProcessStatus restValidateForm( final PwmRequest pwmRequest ) throws IOException, ServletException, PwmUnrecoverableException, ChaiUnavailableException { final UpdateProfileBean updateProfileBean = getBean(pwmRequest); final UpdateAttributesProfile updateAttributesProfile = getProfile(pwmRequest); boolean success = true; String userMessage = Message.getLocalizedMessage(pwmRequest.getLocale(), Message.Success_UpdateForm, pwmRequest.getConfig()); try { // read in the responses from the request final Map<FormConfiguration,String> formValues = readFromJsonRequest(pwmRequest, updateAttributesProfile, updateProfileBean); // verify form meets the form requirements verifyFormAttributes(pwmRequest, formValues, true); updateProfileBean.getFormData().putAll(FormUtility.asStringMap(formValues)); } catch (PwmOperationalException e) { success = false; userMessage = e.getErrorInformation().toUserStr(pwmRequest.getPwmSession(), pwmRequest.getPwmApplication()); } final LinkedHashMap<String, String> outputMap = new LinkedHashMap<>(); outputMap.put("version", "1"); outputMap.put("message", userMessage); outputMap.put("success", String.valueOf(success)); pwmRequest.outputJsonResult(new RestResultBean(outputMap)); return ProcessStatus.Halt; } @ActionHandler(action = "unConfirm") ProcessStatus handleUnconfirm( final PwmRequest pwmRequest ) throws PwmUnrecoverableException { final UpdateProfileBean updateProfileBean = getBean(pwmRequest); updateProfileBean.setFormSubmitted(false); updateProfileBean.setConfirmationPassed(false); updateProfileBean.clearTokenVerificationProgress(); return ProcessStatus.Continue; } @ActionHandler(action = "agree") ProcessStatus handleAgreeRequest(final PwmRequest pwmRequest) throws ServletException, IOException, PwmUnrecoverableException, ChaiUnavailableException { LOGGER.debug(pwmRequest, "user accepted agreement"); final UpdateProfileBean updateProfileBean = getBean(pwmRequest); if (!updateProfileBean.isAgreementPassed()) { updateProfileBean.setAgreementPassed(true); final AuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createUserAuditRecord( AuditEvent.AGREEMENT_PASSED, pwmRequest.getUserInfoIfLoggedIn(), pwmRequest.getSessionLabel(), "UpdateProfile" ); pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord); } return ProcessStatus.Continue; } @ActionHandler(action = "confirm") ProcessStatus handleConfirmRequest(final PwmRequest pwmRequest) throws PwmUnrecoverableException { final UpdateProfileBean updateProfileBean = getBean(pwmRequest); updateProfileBean.setConfirmationPassed(true); return ProcessStatus.Continue; } @ActionHandler(action = "updateProfile") ProcessStatus handleUpdateProfileRequest( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException { final UpdateProfileBean updateProfileBean = getBean(pwmRequest); final UpdateAttributesProfile updateAttributesProfile = getProfile(pwmRequest); try { readFormParametersFromRequest(pwmRequest, updateAttributesProfile, updateProfileBean); } catch (PwmOperationalException e) { LOGGER.error(pwmRequest, e.getMessage()); setLastError(pwmRequest, e.getErrorInformation()); } updateProfileBean.setFormSubmitted(true); return ProcessStatus.Continue; } protected void nextStep( final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException, ChaiUnavailableException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final UpdateProfileBean updateProfileBean = getBean(pwmRequest); final UpdateAttributesProfile updateAttributesProfile = getProfile(pwmRequest); final PwmSession pwmSession = pwmRequest.getPwmSession(); final String updateProfileAgreementText = updateAttributesProfile.readSettingAsLocalizedString( PwmSetting.UPDATE_PROFILE_AGREEMENT_MESSAGE, pwmSession.getSessionStateBean().getLocale() ); if (updateProfileAgreementText != null && updateProfileAgreementText.length() > 0) { if (!updateProfileBean.isAgreementPassed()) { final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication()); final String expandedText = macroMachine.expandMacros(updateProfileAgreementText); pwmRequest.setAttribute(PwmRequestAttribute.AgreementText, expandedText); pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_AGREEMENT); return; } } //make sure there is form data in the bean. if (!updateProfileBean.isFormLdapLoaded()) { updateProfileBean.getFormData().clear(); updateProfileBean.getFormData().putAll((formDataFromLdap(pwmRequest, updateAttributesProfile))); updateProfileBean.setFormLdapLoaded(true); forwardToForm(pwmRequest, updateAttributesProfile, updateProfileBean); return; } if (!updateProfileBean.isFormSubmitted()) { forwardToForm(pwmRequest, updateAttributesProfile, updateProfileBean); return; } // validate the form data. try { // verify form meets the form requirements final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration, String> formValues = FormUtility.readFormValuesFromMap(updateProfileBean.getFormData(), formFields, pwmRequest.getLocale()); verifyFormAttributes(pwmRequest, formValues, true); } catch (PwmException e) { LOGGER.error(pwmSession, e.getMessage()); setLastError(pwmRequest, e.getErrorInformation()); forwardToForm(pwmRequest, updateAttributesProfile, updateProfileBean); return; } final boolean requireConfirmation = updateAttributesProfile.readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_SHOW_CONFIRMATION); if (requireConfirmation && !updateProfileBean.isConfirmationPassed()) { forwardToConfirmForm(pwmRequest, updateAttributesProfile, updateProfileBean); return; } final Set<TokenVerificationProgress.TokenChannel> requiredVerifications = determineTokenPhaseRequired(pwmRequest, updateProfileBean, updateAttributesProfile); if (requiredVerifications != null) { for (final TokenVerificationProgress.TokenChannel tokenChannel : requiredVerifications) { if (requiredVerifications.contains(tokenChannel)) { if (!updateProfileBean.getTokenVerificationProgress().getIssuedTokens().contains(tokenChannel)) { initializeToken(pwmRequest, updateProfileBean, tokenChannel); } if (!updateProfileBean.getTokenVerificationProgress().getPassedTokens().contains(tokenChannel)) { updateProfileBean.getTokenVerificationProgress().setPhase(tokenChannel); pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_ENTER_CODE); return; } } } } try { // write the form values final ChaiUser theUser = pwmSession.getSessionManager().getActor(pwmApplication); doProfileUpdate(pwmRequest, updateProfileBean.getFormData(), theUser); pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_UpdateProfile); return; } catch (PwmException e) { LOGGER.error(pwmSession, e.getMessage()); setLastError(pwmRequest, e.getErrorInformation()); } catch (ChaiException e) { final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UPDATE_ATTRS_FAILURE,e.toString()); LOGGER.error(pwmSession, errorInformation.toDebugStr()); setLastError(pwmRequest, errorInformation); } forwardToForm(pwmRequest, updateAttributesProfile, updateProfileBean); } @Override public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException { if (!pwmRequest.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) { pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "Setting " + PwmSetting.UPDATE_PROFILE_ENABLE.toMenuLocationDebug(null,null) + " is not enabled.")); return ProcessStatus.Halt; } final UpdateAttributesProfile updateAttributesProfile = getProfile(pwmRequest); if (updateAttributesProfile == null) { pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_NO_PROFILE_ASSIGNED)); return ProcessStatus.Halt; } return ProcessStatus.Continue; } final Map<FormConfiguration,String> readFormParametersFromRequest( final PwmRequest pwmRequest, final UpdateAttributesProfile updateAttributesProfile, final UpdateProfileBean updateProfileBean ) throws PwmUnrecoverableException, PwmDataValidationException, ChaiUnavailableException { final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); //read the values from the request final Map<FormConfiguration,String> formValueMap = FormUtility.readFormValuesFromRequest(pwmRequest, formFields, pwmRequest.getLocale()); return updateBeanFormData(formFields, formValueMap, updateProfileBean); } static Map<FormConfiguration,String> readFromJsonRequest( final PwmRequest pwmRequest, final UpdateAttributesProfile updateAttributesProfile, final UpdateProfileBean updateProfileBean ) throws PwmDataValidationException, PwmUnrecoverableException, IOException { final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration,String> formValueMap = FormUtility.readFormValuesFromMap(pwmRequest.readBodyAsJsonStringMap(), formFields, pwmRequest.getLocale()); return updateBeanFormData(formFields, formValueMap, updateProfileBean); } static Map<FormConfiguration,String> updateBeanFormData( final List<FormConfiguration> formFields, final Map<FormConfiguration,String> formValueMap, final UpdateProfileBean updateProfileBean ) { final LinkedHashMap<FormConfiguration,String> newFormValueMap = new LinkedHashMap<>(); for (final FormConfiguration formConfiguration : formFields) { if (formConfiguration.isReadonly()) { final String existingValue = updateProfileBean.getFormData().get(formConfiguration.getName()); newFormValueMap.put(formConfiguration, existingValue); } else { newFormValueMap.put(formConfiguration, formValueMap.get(formConfiguration)); } } updateProfileBean.getFormData().clear(); updateProfileBean.getFormData().putAll(FormUtility.asStringMap(newFormValueMap)); return newFormValueMap; } public static void doProfileUpdate( final PwmRequest pwmRequest, final Map<String,String> formValues, final ChaiUser theUser ) throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final PwmSession pwmSession = pwmRequest.getPwmSession(); final UserInfoBean uiBean = pwmRequest.getPwmSession().getUserInfoBean(); final UpdateAttributesProfile updateAttributesProfile = pwmRequest.getPwmSession().getSessionManager().getUpdateAttributeProfile(pwmApplication); final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration, String> formMap = FormUtility.readFormValuesFromMap(formValues, formFields, pwmRequest.getLocale()); // verify form meets the form requirements (may be redundant, but shouldn't hurt) verifyFormAttributes(pwmRequest, formMap, false); // write values. LOGGER.info("updating profile for " + pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()); pwmRequest.getPwmSession().getSessionManager().getChaiProvider(); LdapOperationsHelper.writeFormValuesToLdap(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), theUser, formMap, false); final UserIdentity userIdentity = uiBean.getUserIdentity(); // re-populate the uiBean because we have changed some values. final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel()); userStatusReader.populateActorUserInfoBean( pwmRequest.getPwmSession(), userIdentity ); // clear cached read attributes. pwmRequest.getPwmSession().getSessionManager().clearUserDataReader(); { // execute configured actions final List<ActionConfiguration> actions = updateAttributesProfile.readSettingAsAction(PwmSetting.UPDATE_PROFILE_WRITE_ATTRIBUTES); if (actions != null && !actions.isEmpty()) { LOGGER.debug(pwmRequest, "executing configured actions to user " + userIdentity); final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings(pwmApplication, userIdentity) .setExpandPwmMacros(true) .setMacroMachine(pwmSession.getSessionManager().getMacroMachine(pwmApplication)) .createActionExecutor(); actionExecutor.executeActions(actions, pwmSession); } } sendProfileUpdateEmailNotice(pwmSession,pwmApplication); // mark the event log pwmApplication.getAuditManager().submit(AuditEvent.UPDATE_PROFILE, pwmSession.getUserInfoBean(), pwmSession); // mark the uiBean so we user isn't recycled to the update profile page by the CommandServlet uiBean.setRequiresUpdateProfile(false); // clear out the updateProfileBean pwmApplication.getSessionStateService().clearBean(pwmRequest, UpdateProfileBean.class); // success, so forward to success page pwmApplication.getStatisticsManager().incrementValue(Statistic.UPDATE_ATTRIBUTES); } private static void verifyFormAttributes( final PwmRequest pwmRequest, final Map<FormConfiguration, String> formValues, final boolean allowResultCaching ) throws PwmOperationalException, PwmUnrecoverableException { final Locale userLocale = pwmRequest.getLocale(); // see if the values meet form requirements. FormUtility.validateFormValues(pwmRequest.getConfig(), formValues, userLocale); // check unique fields against ldap FormUtility.validateFormValueUniqueness( pwmRequest.getPwmApplication(), formValues, userLocale, Collections.singletonList(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()), allowResultCaching ); } private static void sendProfileUpdateEmailNotice( final PwmSession pwmSession, final PwmApplication pwmApplication ) throws PwmUnrecoverableException, ChaiUnavailableException { final Configuration config = pwmApplication.getConfig(); final Locale locale = pwmSession.getSessionStateBean().getLocale(); final EmailItemBean configuredEmailSetting = config.readSettingAsEmail(PwmSetting.EMAIL_UPDATEPROFILE, locale); if (configuredEmailSetting == null) { LOGGER.debug(pwmSession, "skipping send profile update email for '" + pwmSession.getUserInfoBean().getUserIdentity() + "' no email configured"); return; } pwmApplication.getEmailQueue().submitEmail( configuredEmailSetting, pwmSession.getUserInfoBean(), pwmSession.getSessionManager().getMacroMachine(pwmApplication) ); } private static void forwardToForm(final PwmRequest pwmRequest, final UpdateAttributesProfile updateAttributesProfile, final UpdateProfileBean updateProfileBean) throws ServletException, PwmUnrecoverableException, IOException { final List<FormConfiguration> form = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration,String> formValueMap = formMapFromBean(updateAttributesProfile, updateProfileBean); pwmRequest.addFormInfoToRequestAttr(form, formValueMap, false, false); pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES); } private static void forwardToConfirmForm(final PwmRequest pwmRequest, final UpdateAttributesProfile updateAttributesProfile, final UpdateProfileBean updateProfileBean) throws ServletException, PwmUnrecoverableException, IOException { final List<FormConfiguration> form = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration,String> formValueMap = formMapFromBean(updateAttributesProfile, updateProfileBean); pwmRequest.addFormInfoToRequestAttr(form, formValueMap, true, false); pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_CONFIRM); } private static Map<FormConfiguration, String> formMapFromBean(final UpdateAttributesProfile updateAttributesProfile, final UpdateProfileBean updateProfileBean) throws PwmUnrecoverableException { final List<FormConfiguration> form = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration, String> formValueMap = new LinkedHashMap<>(); for (final FormConfiguration formConfiguration : form) { formValueMap.put( formConfiguration, updateProfileBean.getFormData().keySet().contains(formConfiguration.getName()) ? updateProfileBean.getFormData().get(formConfiguration.getName()) : "" ); } return formValueMap; } private static Map<String,String> formDataFromLdap(final PwmRequest pwmRequest, final UpdateAttributesProfile updateAttributesProfile) throws PwmUnrecoverableException { final UserDataReader userDataReader = pwmRequest.getPwmSession().getSessionManager().getUserDataReader(pwmRequest.getPwmApplication()); final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM); final Map<FormConfiguration, String> formMap = new LinkedHashMap<>(); FormUtility.populateFormMapFromLdap(formFields, pwmRequest.getSessionLabel(), formMap, userDataReader); return FormUtility.asStringMap(formMap); } private static Set<TokenVerificationProgress.TokenChannel> determineTokenPhaseRequired( final PwmRequest pwmRequest, final UpdateProfileBean updateProfileBean, final UpdateAttributesProfile updateAttributesProfile ) throws PwmUnrecoverableException { final Set<TokenVerificationProgress.TokenChannel> returnObj = new HashSet<>(); final LdapProfile ldapProfile = pwmRequest.getUserInfoIfLoggedIn().getLdapProfile(pwmRequest.getConfig()); final Map<String,String> userFormData = updateProfileBean.getFormData(); Map<String,String> ldapData = null; if (updateAttributesProfile.readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_EMAIL_VERIFICATION)) { final String emailAddressAttribute = ldapProfile.readSettingAsString(PwmSetting.EMAIL_USER_MAIL_ATTRIBUTE); if (userFormData.containsKey(emailAddressAttribute)) { ldapData = formDataFromLdap(pwmRequest, updateAttributesProfile); if (userFormData.get(emailAddressAttribute) != null && !userFormData.get(emailAddressAttribute).equalsIgnoreCase(ldapData.get(emailAddressAttribute))) { returnObj.add(TokenVerificationProgress.TokenChannel.EMAIL); } } else { LOGGER.warn(pwmRequest, "email verification enabled, but email attribute '" + emailAddressAttribute + "' is not in update form"); } } if (updateAttributesProfile.readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_SMS_VERIFICATION)) { final String phoneNumberAttribute = ldapProfile.readSettingAsString(PwmSetting.SMS_USER_PHONE_ATTRIBUTE); if (userFormData.containsKey(phoneNumberAttribute)) { if (ldapData == null) { ldapData = formDataFromLdap(pwmRequest, updateAttributesProfile); } if (userFormData.get(phoneNumberAttribute) != null && !userFormData.get(phoneNumberAttribute).equalsIgnoreCase(ldapData.get(phoneNumberAttribute))) { returnObj.add(TokenVerificationProgress.TokenChannel.SMS); } } else { LOGGER.warn(pwmRequest, "sms verification enabled, but phone attribute '" + phoneNumberAttribute + "' is not in update form"); } } return returnObj; } private void initializeToken( final PwmRequest pwmRequest, final UpdateProfileBean updateProfileBean, final TokenVerificationProgress.TokenChannel tokenType ) throws PwmUnrecoverableException { final PwmSession pwmSession = pwmRequest.getPwmSession(); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); if (pwmApplication.getConfig().getTokenStorageMethod() == TokenStorageMethod.STORE_LDAP) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{ "cannot generate new user tokens when storage type is configured as STORE_LDAP.", })); } final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmApplication); final Configuration config = pwmApplication.getConfig(); final LdapProfile ldapProfile = pwmRequest.getUserInfoIfLoggedIn().getLdapProfile(pwmRequest.getConfig()); switch (tokenType) { case SMS: { final String telephoneNumberAttribute = ldapProfile.readSettingAsString(PwmSetting.SMS_USER_PHONE_ATTRIBUTE); final String toNum = updateProfileBean.getFormData().get(telephoneNumberAttribute); final String tokenKey; try { final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload( TokenType.UPDATE_SMS, Collections.emptyMap(), pwmRequest.getUserInfoIfLoggedIn(), Collections.singleton(toNum) ); tokenKey = pwmApplication.getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel()); } catch (PwmOperationalException e) { throw new PwmUnrecoverableException(e.getErrorInformation()); } final String message = config.readSettingAsLocalizedString(PwmSetting.SMS_UPDATE_PROFILE_TOKEN_TEXT, pwmSession.getSessionStateBean().getLocale()); try { TokenService.TokenSender.sendSmsToken(pwmApplication, null, macroMachine, toNum, message, tokenKey); } catch (Exception e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN)); } updateProfileBean.getTokenVerificationProgress().getIssuedTokens().add(TokenVerificationProgress.TokenChannel.SMS); updateProfileBean.getTokenVerificationProgress().setTokenDisplayText(toNum); updateProfileBean.getTokenVerificationProgress().setPhase(TokenVerificationProgress.TokenChannel.SMS); } break; case EMAIL: { final EmailItemBean configuredEmailSetting = config.readSettingAsEmail( PwmSetting.EMAIL_UPDATEPROFILE_VERIFICATION, pwmRequest.getLocale() ); final String emailAddressAttribute = ldapProfile.readSettingAsString(PwmSetting.EMAIL_USER_MAIL_ATTRIBUTE); final String toAddress = updateProfileBean.getFormData().get(emailAddressAttribute); final String tokenKey; try { final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload( TokenType.UPDATE_EMAIL, Collections.<String,String>emptyMap(), pwmRequest.getUserInfoIfLoggedIn(), Collections.singleton(toAddress) ); tokenKey = pwmApplication.getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel()); } catch (PwmOperationalException e) { throw new PwmUnrecoverableException(e.getErrorInformation()); } updateProfileBean.getTokenVerificationProgress().getIssuedTokens().add(TokenVerificationProgress.TokenChannel.EMAIL); updateProfileBean.getTokenVerificationProgress().setPhase(TokenVerificationProgress.TokenChannel.EMAIL); updateProfileBean.getTokenVerificationProgress().setTokenDisplayText(toAddress); final EmailItemBean emailItemBean = new EmailItemBean( toAddress, configuredEmailSetting.getFrom(), configuredEmailSetting.getSubject(), configuredEmailSetting.getBodyPlain().replace("%TOKEN%", tokenKey), configuredEmailSetting.getBodyHtml().replace("%TOKEN%", tokenKey)); try { TokenService.TokenSender.sendEmailToken(pwmApplication, null, macroMachine, emailItemBean, toAddress, tokenKey); } catch (Exception e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN)); } } break; default: LOGGER.error("Unimplemented token purpose: " + tokenType); updateProfileBean.getTokenVerificationProgress().setPhase(null); } } }