/*
* 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.helpdesk;
import com.novell.ldapchai.ChaiUser;
import com.novell.ldapchai.exception.ChaiError;
import com.novell.ldapchai.exception.ChaiException;
import com.novell.ldapchai.exception.ChaiOperationException;
import com.novell.ldapchai.exception.ChaiPasswordPolicyException;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import com.novell.ldapchai.provider.ChaiProvider;
import password.pwm.AppProperty;
import password.pwm.PwmApplication;
import password.pwm.PwmConstants;
import password.pwm.bean.EmailItemBean;
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.PwmSetting;
import password.pwm.config.option.HelpdeskClearResponseMode;
import password.pwm.config.option.HelpdeskUIMode;
import password.pwm.config.option.IdentityVerificationMethod;
import password.pwm.config.option.MessageSendMethod;
import password.pwm.config.profile.HelpdeskProfile;
import password.pwm.error.ErrorInformation;
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.PwmHttpRequestWrapper;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmRequestAttribute;
import password.pwm.http.PwmSession;
import password.pwm.http.servlet.AbstractPwmServlet;
import password.pwm.http.servlet.ControlledPwmServlet;
import password.pwm.i18n.Message;
import password.pwm.ldap.LdapOperationsHelper;
import password.pwm.ldap.LdapPermissionTester;
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.search.UserSearchResults;
import password.pwm.svc.event.AuditEvent;
import password.pwm.svc.event.AuditRecordFactory;
import password.pwm.svc.event.HelpdeskAuditRecord;
import password.pwm.svc.intruder.IntruderManager;
import password.pwm.svc.stats.Statistic;
import password.pwm.svc.stats.StatisticsManager;
import password.pwm.svc.token.TokenService;
import password.pwm.util.java.JavaHelper;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.java.TimeDuration;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.macro.MacroMachine;
import password.pwm.util.operations.ActionExecutor;
import password.pwm.util.operations.OtpService;
import password.pwm.util.operations.otp.OTPUserRecord;
import password.pwm.util.secure.SecureService;
import password.pwm.ws.server.RestResultBean;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
*
* Admin interaction servlet for reset user passwords.
*
* @author BoAnSen
*
* */
@WebServlet(
name="HelpdeskServlet",
urlPatterns = {
PwmConstants.URL_PREFIX_PRIVATE + "/helpdesk",
PwmConstants.URL_PREFIX_PRIVATE + "/Helpdesk",
}
)
public class HelpdeskServlet extends ControlledPwmServlet {
private static final PwmLogger LOGGER = PwmLogger.forClass(HelpdeskServlet.class);
public enum HelpdeskAction implements AbstractPwmServlet.ProcessAction {
unlockIntruder(HttpMethod.POST),
clearOtpSecret(HttpMethod.POST),
search(HttpMethod.POST),
detail(HttpMethod.POST),
executeAction(HttpMethod.POST),
deleteUser(HttpMethod.POST),
validateOtpCode(HttpMethod.POST),
sendVerificationToken(HttpMethod.POST),
verifyVerificationToken(HttpMethod.POST),
clientData(HttpMethod.GET),
checkVerification(HttpMethod.POST),
showVerifications(HttpMethod.POST),
validateAttributes(HttpMethod.POST),
;
private final HttpMethod method;
HelpdeskAction(final HttpMethod method)
{
this.method = method;
}
public Collection<HttpMethod> permittedMethods()
{
return Collections.singletonList(method);
}
}
public Class<? extends ProcessAction> getProcessActionsClass() {
return HelpdeskAction.class;
}
private HelpdeskProfile getHelpdeskRProfile(final PwmRequest pwmRequest) {
return pwmRequest.getPwmSession().getSessionManager().getHelpdeskProfile(pwmRequest.getPwmApplication());
}
@Override
protected void nextStep(final PwmRequest pwmRequest)
throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled, !helpdeskProfile.readRequiredVerificationMethods().isEmpty());
pwmRequest.forwardToJsp(JspUrl.HELPDESK_SEARCH);
}
@Override
public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
if (!pwmRequest.isAuthenticated()) {
pwmRequest.respondWithError(PwmError.ERROR_AUTHENTICATION_REQUIRED.toInfo());
return ProcessStatus.Halt;
}
if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE)) {
pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "Setting " + PwmSetting.HELPDESK_ENABLE.toMenuLocationDebug(null,null) + " is not enabled."));
return ProcessStatus.Halt;
}
final HelpdeskProfile helpdeskProfile = pwmRequest.getPwmSession().getSessionManager().getHelpdeskProfile(pwmApplication);
if (helpdeskProfile == null) {
pwmRequest.respondWithError(PwmError.ERROR_UNAUTHORIZED.toInfo());
return ProcessStatus.Halt;
}
return ProcessStatus.Continue;
}
@ActionHandler(action = "clientData")
private ProcessStatus restClientData(final PwmRequest pwmRequest)
throws IOException, PwmUnrecoverableException {
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final HelpdeskClientDataBean returnValues = new HelpdeskClientDataBean();
{ // search page
final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
final Map<String, String> searchColumns = new LinkedHashMap<>();
for (final FormConfiguration formConfiguration : searchForm) {
searchColumns.put(formConfiguration.getName(), formConfiguration.getLabel(pwmRequest.getLocale()));
}
returnValues.setHelpdesk_search_columns(searchColumns);
}
{ /// detail page
returnValues.setHelpdesk_setting_maskPasswords(helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_PASSWORD_MASKVALUE));
returnValues.setHelpdesk_setting_clearResponses(helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_CLEAR_RESPONSES,HelpdeskClearResponseMode.class));
returnValues.setHelpdesk_setting_PwUiMode(helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_SET_PASSWORD_MODE,HelpdeskUIMode.class));
returnValues.setHelpdesk_setting_tokenSendMethod(helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_TOKEN_SEND_METHOD, MessageSendMethod.class));
}
{ //actions
final List<ActionConfiguration> actionConfigurations = helpdeskProfile.readSettingAsAction(PwmSetting.HELPDESK_ACTIONS);
final Map<String,HelpdeskClientDataBean.ActionInformation> actions = new LinkedHashMap<>();
for (final ActionConfiguration actionConfiguration : actionConfigurations) {
final HelpdeskClientDataBean.ActionInformation actionInformation = new HelpdeskClientDataBean.ActionInformation();
actionInformation.setName(actionConfiguration.getName());
actionInformation.setDescription(actionConfiguration.getDescription());
actions.put(actionConfiguration.getName(), actionInformation);
}
returnValues.setActions(actions);
}
{
final Map<String,Collection<IdentityVerificationMethod>> verificationMethodsMap = new HashMap<>();
verificationMethodsMap.put("optional", helpdeskProfile.readOptionalVerificationMethods());
verificationMethodsMap.put("required", helpdeskProfile.readRequiredVerificationMethods());
returnValues.setVerificationMethods(verificationMethodsMap);
}
{
final List<FormConfiguration> attributeVerificationForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_VERIFICATION_FORM);
final List<HelpdeskClientDataBean.FormInformation> formInformations = new ArrayList<>();
if (attributeVerificationForm != null) {
for (final FormConfiguration formConfiguration : attributeVerificationForm) {
final HelpdeskClientDataBean.FormInformation formInformation = new HelpdeskClientDataBean.FormInformation();
formInformation.setName(formConfiguration.getName());
final String label = formConfiguration.getLabel(pwmRequest.getLocale());
formInformation.setLabel((label != null && !label.isEmpty()) ? label : formConfiguration.getName());
formInformations.add(formInformation);
}
}
returnValues.setVerificationForm(formInformations);
}
final RestResultBean restResultBean = new RestResultBean(returnValues);
LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "executeAction")
private ProcessStatus processExecuteActionRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final String userKey = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation).get("userKey");
if (userKey == null || userKey.length() < 1) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
setLastError(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
LOGGER.debug(pwmRequest, "received executeAction request for user " + userIdentity.toString());
final List<ActionConfiguration> actionConfigurations = helpdeskProfile.readSettingAsAction(PwmSetting.HELPDESK_ACTIONS);
final String requestedName = pwmRequest.readParameterAsString("name");
ActionConfiguration action = null;
for (final ActionConfiguration loopAction : actionConfigurations) {
if (requestedName !=null && requestedName.equals(loopAction.getName())) {
action = loopAction;
break;
}
}
if (action == null) {
final String errorMsg = "request to execute unknown action: " + requestedName;
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,errorMsg);
LOGGER.debug(pwmRequest, errorInformation.toDebugStr());
final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
// check if user should be seen by actor
checkIfUserIdentityViewable(pwmRequest, helpdeskProfile, userIdentity);
final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
try {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final ChaiUser chaiUser = useProxy ?
pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity) :
pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity);
final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest, userIdentity);
final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings(pwmRequest.getPwmApplication(),chaiUser)
.setExpandPwmMacros(true)
.setMacroMachine(macroMachine)
.createActionExecutor();
actionExecutor.executeAction(action,pwmRequest.getPwmSession());
// mark the event log
{
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_ACTION,
pwmSession.getUserInfoBean().getUserIdentity(),
action.getName(),
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest.getLocale(), pwmRequest.getConfig(), Message.Success_Action, action.getName());
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
} catch (PwmOperationalException e) {
LOGGER.error(pwmRequest, e.getErrorInformation().toDebugStr());
final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
}
@ActionHandler(action = "deleteUser")
private ProcessStatus restDeleteUserRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
final PwmSession pwmSession = pwmRequest.getPwmSession();
final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey.length() < 1) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
setLastError(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmApplication);
LOGGER.info(pwmSession, "received deleteUser request by " + pwmSession.getUserInfoBean().getUserIdentity().toString() + " for user " + userIdentity.toString());
// check if user should be seen by actor
checkIfUserIdentityViewable(pwmRequest, helpdeskProfile, userIdentity);
// read the userID for later logging.
String userID = null;
try {
userID = LdapOperationsHelper.readLdapUsernameValue(pwmApplication, userIdentity);
} catch (ChaiOperationException e) {
LOGGER.warn(pwmSession, "unable to read username of deleted user while creating audit record");
}
// execute user delete operation
final ChaiProvider provider = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY)
? pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID())
: pwmSession.getSessionManager().getChaiProvider();
try {
provider.deleteEntry(userIdentity.getUserDN());
} catch (ChaiOperationException e) {
final String errorMsg = "error while attempting to delete user " + userIdentity.toString() + ", error: " + e.getMessage();
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
LOGGER.debug(pwmRequest, errorMsg);
pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
return ProcessStatus.Halt;
}
// mark the event log
{
//normally the audit record builder reads the userID while constructing the record, but because the target user is already deleted,
//it will be included here explicitly.
final AuditRecordFactory.AuditUserDefinition auditUserDefinition = new AuditRecordFactory.AuditUserDefinition(
userID,
userIdentity.getUserDN(),
userIdentity.getLdapProfileID()
);
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_DELETE_USER,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
auditUserDefinition,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmApplication.getAuditManager().submit(auditRecord);
}
LOGGER.info(pwmSession, "user " + userIdentity + " has been deleted");
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmSession.getSessionStateBean().getLocale(),Message.Success_Unknown,pwmApplication.getConfig()));
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "detail")
private ProcessStatus processDetailRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey.length() < 1) {
pwmRequest.respondWithError(
new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing"));
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()).canonicalized(pwmRequest.getPwmApplication());
processDetailRequest(pwmRequest, helpdeskProfile, userIdentity);
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VIEW_DETAIL,
pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmRequest.getSessionLabel().getSrcAddress(),
pwmRequest.getSessionLabel().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
return ProcessStatus.Halt;
}
private void processDetailRequest(
final PwmRequest pwmRequest,
final HelpdeskProfile helpdeskProfile,
final UserIdentity userIdentity
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final UserIdentity actorUserIdentity = pwmRequest.getUserInfoIfLoggedIn().canonicalized(pwmRequest.getPwmApplication());
if (actorUserIdentity.canonicalEquals(userIdentity, pwmRequest.getPwmApplication())) {
final String errorMsg = "cannot select self";
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,errorMsg);
LOGGER.debug(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return;
}
LOGGER.trace(pwmRequest, "helpdesk detail view request for user details of " + userIdentity.toString() + " by actor " + actorUserIdentity.toString());
final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(
pwmRequest,
pwmRequest.readParameterAsString(HelpdeskVerificationStateBean.PARAMETER_VERIFICATION_STATE_KEY, PwmHttpRequestWrapper.Flag.BypassValidation)
);
if (!checkIfRequiredVerificationPassed(userIdentity, verificationStateBean, helpdeskProfile)) {
final String errorMsg = "selected user has not been verified";
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,errorMsg);
LOGGER.debug(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return;
}
final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskDetail, helpdeskDetailInfoBean);
if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfoBean() != null) {
final String obfuscatedDN = helpdeskDetailInfoBean.getUserInfoBean().getUserIdentity().toObfuscatedKey(pwmRequest.getPwmApplication());
pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskObfuscatedDN, obfuscatedDN);
pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskUsername, helpdeskDetailInfoBean.getUserInfoBean().getUsername());
}
StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_USER_LOOKUP);
pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled, !helpdeskProfile.readOptionalVerificationMethods().isEmpty());
pwmRequest.forwardToJsp(JspUrl.HELPDESK_DETAIL);
}
@ActionHandler(action = "search")
private ProcessStatus restSearchRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Map<String, String> valueMap = pwmRequest.readBodyAsJsonStringMap();
final String username = valueMap.get("username");
final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
final int maxResults = (int)helpdeskProfile.readSettingAsLong(PwmSetting.HELPDESK_RESULT_LIMIT);
if (username == null ||username.isEmpty()) {
final HelpdeskSearchResultsBean emptyResults = new HelpdeskSearchResultsBean();
emptyResults.setSearchResults(new ArrayList<>());
emptyResults.setSizeExceeded(false);
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setData(emptyResults);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
final UserSearchEngine userSearchEngine = pwmRequest.getPwmApplication().getUserSearchEngine();
final SearchConfiguration searchConfiguration;
{
final SearchConfiguration.SearchConfigurationBuilder builder = SearchConfiguration.builder();
builder.contexts(helpdeskProfile.readSettingAsStringArray(PwmSetting.HELPDESK_SEARCH_BASE));
builder.enableContextValidation(false);
builder.username(username);
builder.enableValueEscaping(false);
builder.filter(getSearchFilter(pwmRequest.getConfig(), helpdeskProfile));
builder.enableSplitWhitespace(true);
if (!useProxy) {
final UserIdentity loggedInUser = pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity();
builder.ldapProfile(loggedInUser.getLdapProfileID());
builder.chaiProvider(getChaiUser(pwmRequest, helpdeskProfile, loggedInUser).getChaiProvider());
}
searchConfiguration = builder.build();
}
final UserSearchResults results;
final boolean sizeExceeded;
try {
final Locale locale = pwmRequest.getLocale();
results = userSearchEngine.performMultiUserSearchFromForm(locale, searchConfiguration, maxResults, searchForm, pwmRequest.getSessionLabel());
sizeExceeded = results.isSizeExceeded();
} catch (PwmOperationalException e) {
final ErrorInformation errorInformation = e.getErrorInformation();
LOGGER.error(pwmRequest, errorInformation);
final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
restResultBean.setData(new ArrayList<Map<String, String>>());
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
final RestResultBean restResultBean = new RestResultBean();
final HelpdeskSearchResultsBean outputData = new HelpdeskSearchResultsBean();
outputData.setSearchResults(results.resultsAsJsonOutput(pwmRequest.getPwmApplication(),pwmRequest.getUserInfoIfLoggedIn()));
outputData.setSizeExceeded(sizeExceeded);
restResultBean.setData(outputData);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "unlockIntruder")
private ProcessStatus restUnlockIntruder(
final PwmRequest pwmRequest
)
throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey.length() < 1) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
if (!helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE_UNLOCK)) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password unlock request, but helpdesk unlock is not enabled");
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation);
return ProcessStatus.Halt;
}
//clear pwm intruder setting.
{
final IntruderManager intruderManager = pwmRequest.getPwmApplication().getIntruderManager();
intruderManager.convenience().clearUserIdentity(userIdentity);
}
// send notice email
sendUnlockNoticeEmail(pwmRequest, helpdeskProfile, userIdentity);
final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
try {
final ChaiUser chaiUser = useProxy ?
pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity) :
pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity);
chaiUser.unlockPassword();
{
// mark the event log
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_UNLOCK_PASSWORD,
pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmRequest.getSessionLabel().getSrcAddress(),
pwmRequest.getSessionLabel().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
} catch (ChaiPasswordPolicyException e) {
final ChaiError passwordError = e.getErrorCode();
final PwmError pwmError = PwmError.forChaiError(passwordError);
pwmRequest.respondWithError(new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError));
LOGGER.trace(pwmRequest, "ChaiPasswordPolicyException was thrown while resetting password: " + e.toString());
return ProcessStatus.Halt;
} catch (ChaiOperationException e) {
final PwmError returnMsg = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
pwmRequest.respondWithError(error);
LOGGER.warn(pwmRequest, "error resetting password for user '" + userIdentity.toDisplayString() + "'' " + error.toDebugStr() + ", " + e.getMessage());
return ProcessStatus.Halt;
}
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "validateOtpCode")
private ProcessStatus restValidateOtpCodeRequest(
final PwmRequest pwmRequest
)
throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Instant startTime = Instant.now();
final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
pwmRequest.readRequestBodyAsString(),
HelpdeskVerificationRequestBean.class
);
final String userKey = helpdeskVerificationRequestBean.getUserKey();
if (userKey == null || userKey.isEmpty()) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
if (!helpdeskProfile.readOptionalVerificationMethods().contains(IdentityVerificationMethod.OTP)) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password otp verification request, but otp verify is not enabled");
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation);
return ProcessStatus.Halt;
}
final String code = helpdeskVerificationRequestBean.getCode();
final OTPUserRecord otpUserRecord = pwmRequest.getPwmApplication().getOtpService().readOTPUserConfiguration(pwmRequest.getSessionLabel(), userIdentity);
try {
final boolean passed = pwmRequest.getPwmApplication().getOtpService().validateToken(
pwmRequest.getPwmSession(),
userIdentity,
otpUserRecord,
code,
false
);
final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(pwmRequest, helpdeskVerificationRequestBean.getVerificationState());
if (passed) {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_OTP,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_VERIFY_OTP);
verificationStateBean.addRecord(userIdentity, IdentityVerificationMethod.OTP);
} else {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_OTP_INCORRECT,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
// add a delay to prevent continuous checks
final long delayMs = Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_VERIFICATION_INVALID_DELAY_MS));
while (TimeDuration.fromCurrent(startTime).isShorterThan(delayMs)) {
JavaHelper.pause(100);
}
final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
final RestResultBean restResultBean = new RestResultBean(responseBean);
pwmRequest.outputJsonResult(restResultBean);
} catch (PwmOperationalException e) {
pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
}
return ProcessStatus.Halt;
}
@ActionHandler(action = "sendVerificationToken")
private ProcessStatus restSendVerificationTokenRequest(
final PwmRequest pwmRequest
)
throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Instant startTime = Instant.now();
final Configuration config = pwmRequest.getConfig();
final Map<String,String> bodyParams = pwmRequest.readBodyAsJsonStringMap();
MessageSendMethod tokenSendMethod = helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_TOKEN_SEND_METHOD, MessageSendMethod.class);
if (tokenSendMethod == MessageSendMethod.CHOICE_SMS_EMAIL) {
final String METHOD_PARAM_NAME = "method";
if (bodyParams != null && bodyParams.containsKey(METHOD_PARAM_NAME)) {
final String methodParam = bodyParams.getOrDefault(METHOD_PARAM_NAME,"");
switch (methodParam) {
case "sms":
tokenSendMethod = MessageSendMethod.SMSONLY;
break;
case "email":
tokenSendMethod = MessageSendMethod.EMAILONLY;
break;
default:
throw new UnsupportedOperationException("unknown tokenSendMethod: " + methodParam);
}
}
if (tokenSendMethod == MessageSendMethod.CHOICE_SMS_EMAIL) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT, "unable to determine appropriate send method, missing method parameter indication from operator");
LOGGER.error(pwmRequest,errorInformation);
pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
return ProcessStatus.Halt;
}
}
final UserIdentity userIdentity = UserIdentity.fromKey(bodyParams.get("userKey"), pwmRequest.getPwmApplication());
final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
if (helpdeskDetailInfoBean == null) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "unable to read helpdesk detail data for specified user");
LOGGER.error(pwmRequest,errorInformation);
pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
return ProcessStatus.Halt;
}
final UserInfoBean userInfoBean = helpdeskDetailInfoBean.getUserInfoBean();
final UserDataReader userDataReader = LdapUserDataReader.appProxiedReader(pwmRequest.getPwmApplication(), userIdentity);
final MacroMachine macroMachine = new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userInfoBean, null, userDataReader);
final String configuredTokenString = config.readAppProperty(AppProperty.HELPDESK_TOKEN_VALUE);
final String tokenKey = macroMachine.expandMacros(configuredTokenString);
final EmailItemBean emailItemBean = config.readSettingAsEmail(PwmSetting.EMAIL_HELPDESK_TOKEN, pwmRequest.getLocale());
final String destEmailAddress = macroMachine.expandMacros(emailItemBean.getTo());
final StringBuilder destDisplayString = new StringBuilder();
if (destEmailAddress != null && !destEmailAddress.isEmpty()) {
if (tokenSendMethod == MessageSendMethod.BOTH || tokenSendMethod == MessageSendMethod.EMAILFIRST || tokenSendMethod == MessageSendMethod.EMAILONLY) {
destDisplayString.append(destEmailAddress);
}
}
if (userInfoBean.getUserSmsNumber() != null && !userInfoBean.getUserSmsNumber().isEmpty()) {
if (tokenSendMethod == MessageSendMethod.BOTH || tokenSendMethod == MessageSendMethod.SMSFIRST || tokenSendMethod == MessageSendMethod.SMSONLY) {
if (destDisplayString.length() > 0) {
destDisplayString.append(", ");
}
destDisplayString.append(userInfoBean.getUserSmsNumber());
}
}
LOGGER.debug(pwmRequest, "generated token code for " + userIdentity.toDelimitedKey());
final String smsMessage = config.readSettingAsLocalizedString(PwmSetting.SMS_HELPDESK_TOKEN_TEXT, pwmRequest.getLocale());
try {
TokenService.TokenSender.sendToken(
pwmRequest.getPwmApplication(),
userInfoBean,
macroMachine,
emailItemBean,
tokenSendMethod,
destEmailAddress,
userInfoBean.getUserSmsNumber(),
smsMessage,
tokenKey
);
} catch (PwmException e) {
LOGGER.error(pwmRequest, e.getErrorInformation());
pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(),pwmRequest));
return ProcessStatus.Halt;
}
StatisticsManager.incrementStat(pwmRequest,Statistic.HELPDESK_TOKENS_SENT);
final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = new HelpdeskVerificationRequestBean();
helpdeskVerificationRequestBean.setDestination(destDisplayString.toString());
helpdeskVerificationRequestBean.setUserKey(bodyParams.get("userKey"));
final HelpdeskVerificationRequestBean.TokenData tokenData = new HelpdeskVerificationRequestBean.TokenData();
tokenData.setToken(tokenKey);
tokenData.setIssueDate(new Date());
final SecureService secureService = pwmRequest.getPwmApplication().getSecureService();
helpdeskVerificationRequestBean.setTokenData(secureService.encryptObjectToString(tokenData));
final RestResultBean restResultBean = new RestResultBean(helpdeskVerificationRequestBean);
pwmRequest.outputJsonResult(restResultBean);
LOGGER.debug(pwmRequest, "helpdesk operator "
+ pwmRequest.getUserInfoIfLoggedIn().toDisplayString()
+ " issued token for verification against user "
+ userIdentity.toDisplayString()
+ " sent to destination(s) "
+ destDisplayString
+ " (" + TimeDuration.fromCurrent(startTime).asCompactString() + ")");
return ProcessStatus.Halt;
}
@ActionHandler(action = "verifyVerificationToken")
private ProcessStatus restVerifyVerificationTokenRequest(
final PwmRequest pwmRequest
)
throws IOException, PwmUnrecoverableException, ServletException
{
final Instant startTime = Instant.now();
final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
pwmRequest.readRequestBodyAsString(),
HelpdeskVerificationRequestBean.class
);
final String token = helpdeskVerificationRequestBean.getCode();
final SecureService secureService = pwmRequest.getPwmApplication().getSecureService();
final HelpdeskVerificationRequestBean.TokenData tokenData = secureService.decryptObject(
helpdeskVerificationRequestBean.getTokenData(),
HelpdeskVerificationRequestBean.TokenData.class
);
final UserIdentity userIdentity = UserIdentity.fromKey(helpdeskVerificationRequestBean.getUserKey(), pwmRequest.getPwmApplication());
if (tokenData == null || tokenData.getIssueDate() == null || tokenData.getToken() == null || tokenData.getToken().isEmpty()) {
final String errorMsg = "token data is corrupted";
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT, errorMsg));
}
final TimeDuration maxTokenAge = new TimeDuration(Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_TOKEN_MAX_AGE)) * 1000);
final Date maxTokenAgeTimestamp = new Date(System.currentTimeMillis() - maxTokenAge.getTotalMilliseconds());
if (tokenData.getIssueDate().before(maxTokenAgeTimestamp)) {
final String errorMsg = "token is older than maximum issue time (" + maxTokenAge.asCompactString() + ")";
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_TOKEN_EXPIRED, errorMsg));
}
final boolean passed = tokenData.getToken().equals(token);
final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(pwmRequest, helpdeskVerificationRequestBean.getVerificationState());
if (passed) {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_TOKEN,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
verificationStateBean.addRecord(userIdentity, IdentityVerificationMethod.TOKEN);
} else {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_TOKEN_INCORRECT,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
// add a delay to prevent continuous checks
final long delayMs = Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_VERIFICATION_INVALID_DELAY_MS));
while (TimeDuration.fromCurrent(startTime).isShorterThan(delayMs)) {
JavaHelper.pause(100);
}
final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
final RestResultBean restResultBean = new RestResultBean(responseBean);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "clearOtpSecret")
private ProcessStatus restClearOtpSecret(
final PwmRequest pwmRequest
)
throws ServletException, IOException, PwmUnrecoverableException, ChaiUnavailableException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
if (!helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_CLEAR_OTP_BUTTON)) {
final String errorMsg = "clear otp request, but helpdesk clear otp button is not enabled";
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg);
LOGGER.error(pwmRequest, errorMsg);
pwmRequest.respondWithError(errorInformation);
return ProcessStatus.Halt;
}
//clear pwm intruder setting.
pwmRequest.getPwmApplication().getIntruderManager().convenience().clearUserIdentity(userIdentity);
try {
final OtpService service = pwmRequest.getPwmApplication().getOtpService();
service.clearOTPUserConfiguration(pwmRequest.getPwmSession(), userIdentity);
{
// mark the event log
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_CLEAR_OTP_SECRET,
pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmRequest.getSessionLabel().getSrcAddress(),
pwmRequest.getSessionLabel().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
} catch (PwmOperationalException e) {
final PwmError returnMsg = e.getError();
final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
pwmRequest.respondWithError(error);
LOGGER.warn(pwmRequest, "error clearing OTP secret for user '" + userIdentity + "'' " + error.toDebugStr() + ", " + e.getMessage());
return ProcessStatus.Halt;
}
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
private static String getSearchFilter(final Configuration configuration, final HelpdeskProfile helpdeskProfile) {
final String configuredFilter = helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_SEARCH_FILTER);
if (configuredFilter != null && !configuredFilter.isEmpty()) {
return configuredFilter;
}
final List<String> defaultObjectClasses = configuration.readSettingAsStringArray(PwmSetting.DEFAULT_OBJECT_CLASSES);
final List<FormConfiguration> searchAttributes = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
final StringBuilder filter = new StringBuilder();
filter.append("(&"); //open AND clause for objectclasses and attributes
for (final String objectClass : defaultObjectClasses) {
filter.append("(objectClass=").append(objectClass).append(")");
}
filter.append("(|"); // open OR clause for attributes
for (final FormConfiguration formConfiguration : searchAttributes) {
if (formConfiguration != null && formConfiguration.getName() != null) {
final String searchAttribute = formConfiguration.getName();
filter.append("(").append(searchAttribute).append("=*").append(PwmConstants.VALUE_REPLACEMENT_USERNAME).append("*)");
}
}
filter.append(")"); // close OR clause
filter.append(")"); // close AND clause
return filter.toString();
}
static ChaiUser getChaiUser(
final PwmRequest pwmRequest,
final HelpdeskProfile helpdeskProfile,
final UserIdentity userIdentity
)
throws ChaiUnavailableException, PwmUnrecoverableException
{
final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
return useProxy ?
pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity) :
pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity);
}
private static void checkIfUserIdentityViewable(
final PwmRequest pwmRequest,
final HelpdeskProfile helpdeskProfile,
final UserIdentity userIdentity
)
throws PwmUnrecoverableException {
final String filterSetting = getSearchFilter(pwmRequest.getConfig(), helpdeskProfile);
String filterString = filterSetting.replace(PwmConstants.VALUE_REPLACEMENT_USERNAME, "*");
while (filterString.contains("**")) {
filterString = filterString.replace("**", "*");
}
final boolean match = LdapPermissionTester.testQueryMatch(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userIdentity, filterString);
if (!match) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "requested userDN is not available within configured search filter"));
}
}
@ActionHandler(action = "checkVerification")
private ProcessStatus restCheckVerification(final PwmRequest pwmRequest)
throws IOException, PwmUnrecoverableException, ServletException {
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
checkIfUserIdentityViewable(pwmRequest, helpdeskProfile, userIdentity);
final String rawVerificationStr = bodyMap.get(HelpdeskVerificationStateBean.PARAMETER_VERIFICATION_STATE_KEY);
final HelpdeskVerificationStateBean state = HelpdeskVerificationStateBean.fromClientString(pwmRequest, rawVerificationStr);
final boolean passed = checkIfRequiredVerificationPassed(userIdentity, state, helpdeskProfile);
final HashMap<String,Object> results = new HashMap<>();
results.put("passed",passed);
final RestResultBean restResultBean = new RestResultBean(results);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
private boolean checkIfRequiredVerificationPassed(final UserIdentity userIdentity, final HelpdeskVerificationStateBean verificationStateBean, final HelpdeskProfile helpdeskProfile) {
final Collection<IdentityVerificationMethod> requiredMethods = helpdeskProfile.readRequiredVerificationMethods();
if (requiredMethods == null || requiredMethods.isEmpty()) {
return true;
}
for (final IdentityVerificationMethod method : requiredMethods) {
if (verificationStateBean.hasRecord(userIdentity, method)) {
return true;
}
}
return false;
}
@ActionHandler(action = "showVerifications")
private ProcessStatus restShowVerifications(final PwmRequest pwmRequest)
throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
{
final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
final String rawVerificationStr = bodyMap.get(HelpdeskVerificationStateBean.PARAMETER_VERIFICATION_STATE_KEY);
final HelpdeskVerificationStateBean state = HelpdeskVerificationStateBean.fromClientString(pwmRequest, rawVerificationStr);
final HashMap<String,Object> results = new HashMap<>();
try {
results.put("records",state.asViewableValidationRecords(pwmRequest.getPwmApplication(), pwmRequest.getLocale()));
} catch (ChaiOperationException e) {
throw PwmUnrecoverableException.fromChaiException(e);
}
final RestResultBean restResultBean = new RestResultBean(results);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
private UserIdentity userIdentityFromMap(final PwmRequest pwmRequest, final Map<String,String> bodyMap) throws PwmUnrecoverableException {
final String userKey = bodyMap.get("userKey");
if (userKey == null || userKey.length() < 1) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
throw new PwmUnrecoverableException(errorInformation);
}
return UserIdentity.fromObfuscatedKey(userKey, pwmRequest.getPwmApplication());
}
@ActionHandler(action = "validateAttributes")
private ProcessStatus restValidateAttributes(final PwmRequest pwmRequest)
throws IOException, PwmUnrecoverableException, ServletException
{
final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
final Instant startTime = Instant.now();
final String bodyString = pwmRequest.readRequestBodyAsString();
final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
bodyString,
HelpdeskVerificationRequestBean.class
);
final UserIdentity userIdentity = UserIdentity.fromKey(helpdeskVerificationRequestBean.getUserKey(), pwmRequest.getPwmApplication());
boolean passed = false;
{
final List<FormConfiguration> verificationForms = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_VERIFICATION_FORM);
if (verificationForms == null || verificationForms.isEmpty()) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG,"attempt to verify ldap attributes with no ldap verification attributes configured");
throw new PwmUnrecoverableException(errorInformation);
}
final Map<String,String> bodyMap = JsonUtil.deserializeStringMap(bodyString);
final ChaiUser chaiUser;
try {
chaiUser = getChaiUser(pwmRequest, helpdeskProfile, userIdentity);
} catch (ChaiUnavailableException e) {
throw new PwmUnrecoverableException(PwmError.forChaiError(e.getErrorCode()));
}
int successCount = 0;
for (final FormConfiguration formConfiguration : verificationForms) {
final String name = formConfiguration.getName();
final String suppliedValue = bodyMap.get(name);
try {
if (chaiUser.compareStringAttribute(name, suppliedValue)) {
successCount++;
}
} catch (ChaiException e) {
LOGGER.error(pwmRequest, "error comparing ldap attribute during verification " + e.getMessage());
}
}
if (successCount == verificationForms.size()) {
passed = true;
}
}
final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(pwmRequest, helpdeskVerificationRequestBean.getVerificationState());
if (passed) {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_ATTRIBUTES,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
verificationStateBean.addRecord(userIdentity, IdentityVerificationMethod.ATTRIBUTES);
} else {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(
AuditEvent.HELPDESK_VERIFY_ATTRIBUTES_INCORRECT,
pwmSession.getUserInfoBean().getUserIdentity(),
null,
userIdentity,
pwmSession.getSessionStateBean().getSrcAddress(),
pwmSession.getSessionStateBean().getSrcHostname()
);
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
// add a delay to prevent continuous checks
final long delayMs = Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_VERIFICATION_INVALID_DELAY_MS));
while (TimeDuration.fromCurrent(startTime).isShorterThan(delayMs)) {
JavaHelper.pause(100);
}
final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
final RestResultBean restResultBean = new RestResultBean(responseBean);
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
private static void sendUnlockNoticeEmail(
final PwmRequest pwmRequest,
final HelpdeskProfile helpdeskProfile,
final UserIdentity userIdentity
)
throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException {
final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
final Configuration config = pwmRequest.getConfig();
final Locale locale = pwmRequest.getLocale();
final EmailItemBean configuredEmailSetting = config.readSettingAsEmail(PwmSetting.EMAIL_HELPDESK_UNLOCK, locale);
if (configuredEmailSetting == null) {
LOGGER.debug(pwmRequest, "skipping send helpdesk unlock notice email for '" + userIdentity + "' no email configured");
return;
}
final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
final MacroMachine macroMachine = new MacroMachine(
pwmApplication,
pwmRequest.getSessionLabel(),
helpdeskDetailInfoBean.getUserInfoBean(),
null,
LdapUserDataReader.appProxiedReader(pwmApplication, userIdentity)
);
pwmApplication.getEmailQueue().submitEmail(
configuredEmailSetting,
helpdeskDetailInfoBean.getUserInfoBean(),
macroMachine
);
}
}