/* * 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.ws.server.rest; import com.novell.ldapchai.ChaiUser; import com.novell.ldapchai.cr.ChaiChallenge; import com.novell.ldapchai.cr.Challenge; import com.novell.ldapchai.cr.ChallengeSet; import com.novell.ldapchai.cr.ResponseSet; import com.novell.ldapchai.cr.bean.ChallengeBean; import password.pwm.Permission; import password.pwm.bean.ResponseInfoBean; import password.pwm.bean.UserIdentity; import password.pwm.config.PwmSetting; import password.pwm.config.profile.ChallengeProfile; import password.pwm.config.profile.HelpdeskProfile; import password.pwm.config.profile.PwmPasswordPolicy; 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.i18n.Message; import password.pwm.ldap.LdapOperationsHelper; import password.pwm.svc.event.AuditEvent; import password.pwm.svc.event.AuditRecordFactory; import password.pwm.svc.event.HelpdeskAuditRecord; import password.pwm.svc.event.UserAuditRecord; import password.pwm.svc.stats.Statistic; import password.pwm.svc.stats.StatisticsManager; import password.pwm.util.operations.CrService; import password.pwm.util.operations.PasswordUtility; import password.pwm.ws.server.RestRequestBean; import password.pwm.ws.server.RestResultBean; import password.pwm.ws.server.RestServerHelper; import password.pwm.ws.server.ServicePermissions; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.Serializable; import java.net.URISyntaxException; import java.time.Instant; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @Path("/challenges") public class RestChallengesServer extends AbstractRestServer { public static class Policy { public List<ChallengeBean> challenges; public List<ChallengeBean> helpdeskChallenges; public int minimumRandoms; } public static class JsonChallengesData implements Serializable { public String username; public List<ChallengeBean> challenges; public List<ChallengeBean> helpdeskChallenges; public int minimumRandoms; public Policy policy; public ResponseInfoBean toResponseInfoBean(final Locale locale, final String csIdentifier) throws PwmOperationalException { final Map<Challenge,String> crMap = new LinkedHashMap<>(); if (challenges != null) { for (final ChallengeBean challengeBean : challenges) { final Challenge challenge = ChaiChallenge.fromChallengeBean(challengeBean); final String answerText = challengeBean.getAnswer().getAnswerText(); if (answerText == null || answerText.length() < 1) { throw new IllegalArgumentException("missing answerText for challenge '" + challenge.getChallengeText() + "'"); } crMap.put(challenge,answerText); } } final Map<Challenge,String> helpdeskCrMap = new LinkedHashMap<>(); if (helpdeskChallenges != null) { for (final ChallengeBean challengeBean : helpdeskChallenges) { final Challenge challenge = ChaiChallenge.fromChallengeBean(challengeBean); final String answerText = challengeBean.getAnswer().getAnswerText(); if (answerText == null || answerText.length() < 1) { throw new IllegalArgumentException("missing answerText for helpdesk challenge '" + challenge.getChallengeText() + "'"); } helpdeskCrMap.put(challenge,answerText); } } final ResponseInfoBean responseInfoBean = new ResponseInfoBean( crMap, helpdeskCrMap, locale, minimumRandoms, csIdentifier, null, null ); responseInfoBean.setTimestamp(Instant.now()); return responseInfoBean; } } @Context HttpServletRequest request; @GET @Produces(MediaType.TEXT_HTML) public javax.ws.rs.core.Response doHtmlRedirect() throws URISyntaxException { return RestServerHelper.doHtmlRedirect(); } @GET @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8") public Response doFormGetChallengeData( @QueryParam("answers") final boolean answers, @QueryParam("helpdesk") final boolean helpdesk, @QueryParam("username") final String username ) throws PwmUnrecoverableException { final RestRequestBean restRequestBean; try { final ServicePermissions servicePermissions = new ServicePermissions(); servicePermissions.setAdminOnly(false); servicePermissions.setAuthRequired(true); servicePermissions.setBlockExternal(true); restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username); } catch (PwmUnrecoverableException e) { return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse(); } try { if (answers && !restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.ENABLE_WEBSERVICES_READANSWERS)) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE,"retrieval of answers is not permitted")); } // gather data final ResponseSet responseSet; final ChallengeSet challengeSet; final ChallengeSet helpdeskChallengeSet; final String outputUsername; if (restRequestBean.getUserIdentity() == null) { final ChaiUser chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication()); final CrService crService = restRequestBean.getPwmApplication().getCrService(); responseSet = crService.readUserResponseSet(restRequestBean.getPwmSession().getLabel(), restRequestBean.getPwmSession().getUserInfoBean().getUserIdentity(), chaiUser); challengeSet = restRequestBean.getPwmSession().getUserInfoBean().getChallengeProfile().getChallengeSet(); helpdeskChallengeSet = restRequestBean.getPwmSession().getUserInfoBean().getChallengeProfile().getHelpdeskChallengeSet(); outputUsername = restRequestBean.getPwmSession().getUserInfoBean().getUserIdentity().getLdapProfileID(); } else { final ChaiUser chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity()); final Locale userLocale = restRequestBean.getPwmSession().getSessionStateBean().getLocale(); final CrService crService = restRequestBean.getPwmApplication().getCrService(); responseSet = crService.readUserResponseSet(restRequestBean.getPwmSession().getLabel(),restRequestBean.getUserIdentity(), chaiUser); final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser( restRequestBean.getPwmApplication(), restRequestBean.getPwmSession().getLabel(), restRequestBean.getUserIdentity(), chaiUser,userLocale ); final ChallengeProfile challengeProfile = crService.readUserChallengeProfile( restRequestBean.getPwmSession().getLabel(), restRequestBean.getUserIdentity(), chaiUser, passwordPolicy, userLocale ); challengeSet = challengeProfile.getChallengeSet(); helpdeskChallengeSet = challengeProfile.getHelpdeskChallengeSet(); outputUsername = restRequestBean.getUserIdentity().toDelimitedKey(); } // build output final JsonChallengesData jsonData = new JsonChallengesData(); { jsonData.username = outputUsername; if (responseSet != null) { jsonData.challenges = responseSet.asChallengeBeans(answers); if (helpdesk) { jsonData.helpdeskChallenges = responseSet.asHelpdeskChallengeBeans(answers); } jsonData.minimumRandoms = responseSet.getChallengeSet().getMinRandomRequired(); } final Policy policy = new Policy(); if (challengeSet != null) { policy.challenges = challengesToBeans(challengeSet.getChallenges()); policy.minimumRandoms = challengeSet.getMinRandomRequired(); } if (helpdeskChallengeSet != null && helpdesk) { policy.helpdeskChallenges = challengesToBeans(helpdeskChallengeSet.getChallenges()); } if (policy.challenges != null || policy.helpdeskChallenges != null) { jsonData.policy = policy; } } // update statistics if (!restRequestBean.isExternal()) { StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES); } final RestResultBean resultBean = new RestResultBean(); resultBean.setData(jsonData); return resultBean.asJsonResponse(); } catch (PwmException e) { return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse(); } catch (Exception e) { final String errorMsg = "unexpected error building json response: " + e.getMessage(); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg); return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse(); } } @POST @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8") @Consumes(MediaType.APPLICATION_JSON) public Response doSetChallengeDataJson( final JsonChallengesData jsonInput ) { final RestRequestBean restRequestBean; try { final ServicePermissions servicePermissions = new ServicePermissions(); servicePermissions.setAdminOnly(false); servicePermissions.setAuthRequired(true); servicePermissions.setBlockExternal(true); restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username); } catch (PwmUnrecoverableException e) { return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse(); } try { if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.SETUP_RESPONSE)) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission")); } final ChaiUser chaiUser; final String userGUID; final String csIdentifer; final UserIdentity userIdentity; final CrService crService = restRequestBean.getPwmApplication().getCrService(); if (restRequestBean.getUserIdentity() == null) { chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication()); userIdentity = restRequestBean.getPwmSession().getUserInfoBean().getUserIdentity(); userGUID = restRequestBean.getPwmSession().getUserInfoBean().getUserGuid(); csIdentifer = restRequestBean.getPwmSession().getUserInfoBean().getChallengeProfile().getChallengeSet().getIdentifier(); } else { userIdentity = restRequestBean.getUserIdentity(); chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),userIdentity); userGUID = LdapOperationsHelper.readLdapGuidValue( restRequestBean.getPwmApplication(), restRequestBean.getPwmSession().getLabel(), userIdentity, false); final ChallengeProfile challengeProfile = crService.readUserChallengeProfile( restRequestBean.getPwmSession().getLabel(), userIdentity, chaiUser, PwmPasswordPolicy.defaultPolicy(), request.getLocale()); csIdentifer = challengeProfile.getChallengeSet().getIdentifier(); } final ResponseInfoBean responseInfoBean = jsonInput.toResponseInfoBean(request.getLocale(), csIdentifer); crService.writeResponses(userIdentity, chaiUser,userGUID,responseInfoBean); // update statistics if (!restRequestBean.isExternal()) { StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES); } final String successMsg = Message.Success_SetupResponse.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig()); final RestResultBean resultBean = new RestResultBean(); resultBean.setError(false); resultBean.setSuccessMessage(successMsg); return resultBean.asJsonResponse(); } catch (PwmException e) { return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse(); } catch (Exception e) { final String errorMsg = "unexpected error reading json input: " + e.getMessage(); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg); return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse(); } } @DELETE @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8") public Response doDeleteChallengeData( @QueryParam("username") final String username ) { final RestRequestBean restRequestBean; try { final ServicePermissions servicePermissions = new ServicePermissions(); servicePermissions.setAdminOnly(false); servicePermissions.setAuthRequired(true); servicePermissions.setBlockExternal(true); servicePermissions.setHelpdeskPermitted(true); restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username); } catch (PwmUnrecoverableException e) { return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse(); } final HelpdeskProfile helpdeskProfile = restRequestBean.getPwmSession().getSessionManager().getHelpdeskProfile(restRequestBean.getPwmApplication()); try { if (restRequestBean.getUserIdentity() == null) { if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.SETUP_RESPONSE)) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission")); } } else { if (helpdeskProfile == null) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission")); } } final ChaiUser chaiUser; final String userGUID; if (restRequestBean.getUserIdentity() == null) { /* clear self */ chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication()); userGUID = restRequestBean.getPwmSession().getUserInfoBean().getUserGuid(); // mark the event log final UserAuditRecord auditRecord = new AuditRecordFactory(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession()).createUserAuditRecord( AuditEvent.CLEAR_RESPONSES, restRequestBean.getPwmSession().getUserInfoBean(), restRequestBean.getPwmSession() ); restRequestBean.getPwmApplication().getAuditManager().submit(auditRecord); } else { /* clear 3rd party (helpdesk) */ final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY); chaiUser = useProxy ? restRequestBean.getPwmApplication().getProxiedChaiUser(restRequestBean.getUserIdentity()) : restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity()); userGUID = LdapOperationsHelper.readLdapGuidValue( restRequestBean.getPwmApplication(), restRequestBean.getPwmSession().getLabel(), restRequestBean.getUserIdentity(), false); // mark the event log final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession()).createHelpdeskAuditRecord( AuditEvent.HELPDESK_CLEAR_RESPONSES, restRequestBean.getPwmSession().getUserInfoBean().getUserIdentity(), null, restRequestBean.getUserIdentity(), restRequestBean.getPwmSession().getSessionStateBean().getSrcAddress(), restRequestBean.getPwmSession().getSessionStateBean().getSrcHostname() ); restRequestBean.getPwmApplication().getAuditManager().submit(auditRecord); } final CrService crService = restRequestBean.getPwmApplication().getCrService(); crService.clearResponses( restRequestBean.getPwmSession().getLabel(), restRequestBean.getUserIdentity(), chaiUser, userGUID ); // update statistics if (!restRequestBean.isExternal()) { StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES); } final String successMsg = Message.Success_Unknown.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig()); final RestResultBean resultBean = new RestResultBean(); resultBean.setError(false); resultBean.setSuccessMessage(successMsg); return resultBean.asJsonResponse(); } catch (PwmException e) { return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse(); } catch (Exception e) { final String errorMsg = "unexpected error delete responses: " + e.getMessage(); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg); return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse(); } } private static List<ChallengeBean> challengesToBeans(final List<Challenge> challenges) { final List<ChallengeBean> returnList = new ArrayList<>(); for (final Challenge challenge : challenges) { returnList.add(challenge.asChallengeBean()); } return returnList; } }