/* * 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.cr.ChaiCrFactory; import com.novell.ldapchai.cr.ChaiResponseSet; import com.novell.ldapchai.cr.Challenge; import com.novell.ldapchai.cr.ChallengeSet; import com.novell.ldapchai.exception.ChaiError; import com.novell.ldapchai.exception.ChaiUnavailableException; import com.novell.ldapchai.exception.ChaiValidationException; import com.novell.ldapchai.provider.ChaiProvider; import password.pwm.Permission; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.bean.ResponseInfoBean; import password.pwm.bean.UserInfoBean; import password.pwm.config.PwmSetting; import password.pwm.config.profile.ChallengeProfile; import password.pwm.error.ErrorInformation; import password.pwm.error.PwmDataValidationException; import password.pwm.error.PwmError; import password.pwm.error.PwmOperationalException; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.HttpMethod; import password.pwm.http.JspUrl; import password.pwm.http.ProcessStatus; import password.pwm.http.PwmRequest; import password.pwm.http.PwmRequestAttribute; import password.pwm.http.PwmSession; import password.pwm.http.bean.SetupResponsesBean; import password.pwm.i18n.Message; import password.pwm.ldap.UserStatusReader; import password.pwm.ldap.auth.AuthenticationType; import password.pwm.svc.event.AuditEvent; import password.pwm.svc.event.AuditRecordFactory; import password.pwm.svc.event.UserAuditRecord; import password.pwm.svc.stats.Statistic; import password.pwm.util.java.JsonUtil; import password.pwm.util.java.TimeDuration; import password.pwm.util.logging.PwmLogger; import password.pwm.ws.server.RestResultBean; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import java.io.IOException; import java.io.Serializable; import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; /** * User interaction servlet for setting up secret question/answer * * @author Jason D. Rivard */ @WebServlet( name="SetupResponsesServlet", urlPatterns={ PwmConstants.URL_PREFIX_PRIVATE + "/setup-responses", PwmConstants.URL_PREFIX_PRIVATE + "/SetupResponses", } ) public class SetupResponsesServlet extends ControlledPwmServlet { private static final PwmLogger LOGGER = PwmLogger.forClass(SetupResponsesServlet.class); public enum SetupResponsesAction implements AbstractPwmServlet.ProcessAction { validateResponses(HttpMethod.POST), setResponses(HttpMethod.POST), setHelpdeskResponses(HttpMethod.POST), confirmResponses(HttpMethod.POST), clearExisting(HttpMethod.POST), changeResponses(HttpMethod.POST), ; private final HttpMethod method; SetupResponsesAction(final HttpMethod method) { this.method = method; } public Collection<HttpMethod> permittedMethods() { return Collections.singletonList(method); } } @Override public Class<? extends ProcessAction> getProcessActionsClass() { return SetupResponsesAction.class; } private SetupResponsesBean getSetupResponseBean(final PwmRequest pwmRequest) throws PwmUnrecoverableException { return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, SetupResponsesBean.class); } @Override public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException { final PwmSession pwmSession = pwmRequest.getPwmSession(); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); if (!pwmSession.isAuthenticated()) { pwmRequest.respondWithError(PwmError.ERROR_AUTHENTICATION_REQUIRED.toInfo()); return ProcessStatus.Halt; } if (pwmSession.getLoginInfoBean().getType() == AuthenticationType.AUTH_WITHOUT_PASSWORD) { throw new PwmUnrecoverableException(PwmError.ERROR_PASSWORD_REQUIRED); } if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.CHALLENGE_ENABLE)) { throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE); } // check to see if the user is permitted to setup responses if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.SETUP_RESPONSE)) { throw new PwmUnrecoverableException(PwmError.ERROR_UNAUTHORIZED); } // check if the locale has changed since first seen. if (pwmSession.getSessionStateBean().getLocale() != pwmApplication.getSessionStateService().getBean(pwmRequest, SetupResponsesBean.class).getUserLocale()) { pwmRequest.getPwmApplication().getSessionStateService().clearBean(pwmRequest, SetupResponsesBean.class); pwmApplication.getSessionStateService().getBean(pwmRequest, SetupResponsesBean.class).setUserLocale(pwmSession.getSessionStateBean().getLocale()); } initializeBean(pwmRequest, setupResponsesBean); // check to see if the user has any challenges assigned final UserInfoBean uiBean = pwmSession.getUserInfoBean(); if (setupResponsesBean.getResponseData().getChallengeSet() == null || setupResponsesBean.getResponseData().getChallengeSet().getChallenges().isEmpty()) { final String errorMsg = "no challenge sets configured for user " + uiBean.getUserIdentity(); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_NO_CHALLENGES, errorMsg); LOGGER.debug(pwmSession, errorInformation); throw new PwmUnrecoverableException(errorInformation); } return ProcessStatus.Continue; } @ActionHandler(action = "confirmResponses") private ProcessStatus processConfirmResponses(final PwmRequest pwmRequest) throws PwmUnrecoverableException { final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); setupResponsesBean.setConfirmed(true); return ProcessStatus.Continue; } @ActionHandler(action = "changeResponses") private ProcessStatus processChangeResponses(final PwmRequest pwmRequest) throws PwmUnrecoverableException { final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); pwmApplication.getSessionStateService().clearBean(pwmRequest, SetupResponsesBean.class); this.initializeBean(pwmRequest, setupResponsesBean); setupResponsesBean.setUserLocale(pwmRequest.getLocale()); return ProcessStatus.Continue; } @ActionHandler(action = "clearExisting") private ProcessStatus handleClearExisting( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, ChaiUnavailableException, IOException { LOGGER.trace(pwmRequest, "request for response clear received"); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final PwmSession pwmSession = pwmRequest.getPwmSession(); try { final String userGUID = pwmSession.getUserInfoBean().getUserGuid(); final ChaiUser theUser = pwmSession.getSessionManager().getActor(pwmApplication); pwmApplication.getCrService().clearResponses(pwmSession.getLabel(), pwmRequest.getUserInfoIfLoggedIn(), theUser, userGUID); final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, pwmRequest.getSessionLabel()); userStatusReader.populateLocaleSpecificUserInfoBean(pwmSession.getUserInfoBean(),pwmRequest.getLocale()); pwmRequest.getPwmApplication().getSessionStateService().clearBean(pwmRequest, SetupResponsesBean.class); // mark the event log final UserAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createUserAuditRecord( AuditEvent.CLEAR_RESPONSES, pwmSession.getUserInfoBean(), pwmSession ); pwmApplication.getAuditManager().submit(auditRecord); pwmRequest.sendRedirect(PwmServletDefinition.SetupResponses); } catch (PwmOperationalException e) { LOGGER.debug(pwmSession, e.getErrorInformation()); setLastError(pwmRequest, e.getErrorInformation()); } return ProcessStatus.Continue; } @ActionHandler(action = "validateResponses") private ProcessStatus restValidateResponses( final PwmRequest pwmRequest ) throws IOException, ServletException, PwmUnrecoverableException, ChaiUnavailableException { final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); final Instant startTime = Instant.now(); final PwmSession pwmSession = pwmRequest.getPwmSession(); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final String responseModeParam = pwmRequest.readParameterAsString("responseMode"); final SetupResponsesBean.SetupData setupData = "helpdesk".equalsIgnoreCase(responseModeParam) ? setupResponsesBean.getHelpdeskResponseData() : setupResponsesBean.getResponseData(); boolean success = true; String userMessage = Message.getLocalizedMessage(pwmSession.getSessionStateBean().getLocale(), Message.Success_ResponsesMeetRules, pwmApplication.getConfig()); try { // read in the responses from the request final Map<Challenge, String> responseMap = readResponsesFromJsonRequest(pwmRequest, setupData); final int minRandomRequiredSetup = setupData.getMinRandomSetup(); pwmApplication.getCrService().validateResponses(setupData.getChallengeSet(), responseMap, minRandomRequiredSetup); generateResponseInfoBean(pwmRequest, setupData.getChallengeSet(), responseMap, Collections.emptyMap()); } catch (PwmDataValidationException e) { success = false; userMessage = e.getErrorInformation().toUserStr(pwmSession, pwmApplication); } final ValidationResponseBean validationResponseBean = new ValidationResponseBean(userMessage,success); final RestResultBean restResultBean = new RestResultBean(validationResponseBean); LOGGER.trace(pwmRequest,"completed rest validate response in " + TimeDuration.fromCurrent(startTime).asCompactString() + ", result=" + JsonUtil.serialize(restResultBean)); pwmRequest.outputJsonResult(restResultBean); return ProcessStatus.Halt; } @ActionHandler(action = "setHelpdeskResponses") private ProcessStatus processSetHelpdeskResponses(final PwmRequest pwmRequest) throws ChaiUnavailableException, PwmUnrecoverableException, ServletException, IOException { setupResponses(pwmRequest, true); return ProcessStatus.Continue; } @ActionHandler(action = "setResponses") private ProcessStatus processSetResponses(final PwmRequest pwmRequest) throws ChaiUnavailableException, PwmUnrecoverableException, ServletException, IOException { setupResponses(pwmRequest, false); return ProcessStatus.Continue; } @Override protected void nextStep(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException { final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); initializeBean(pwmRequest, setupResponsesBean); pwmRequest.setAttribute(PwmRequestAttribute.ModuleBean, setupResponsesBean); pwmRequest.setAttribute(PwmRequestAttribute.ModuleBean_String, pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(setupResponsesBean)); pwmRequest.setAttribute(PwmRequestAttribute.SetupResponses_ResponseInfo, pwmRequest.getPwmSession().getUserInfoBean().getResponseInfoBean()); if (setupResponsesBean.isHasExistingResponses() && !pwmRequest.getPwmSession().getUserInfoBean().isRequiresResponseConfig()) { pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES_EXISTING); return; } if (!setupResponsesBean.isResponsesSatisfied()) { pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES); return; } if (!setupResponsesBean.isHelpdeskResponsesSatisfied()) { if (setupResponsesBean.getHelpdeskResponseData().getChallengeSet() == null || setupResponsesBean.getHelpdeskResponseData().getChallengeSet().getChallenges().isEmpty()) { setupResponsesBean.setHelpdeskResponsesSatisfied(true); } else { pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES_HELPDESK); return; } } if (pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.CHALLENGE_SHOW_CONFIRMATION)) { if (!setupResponsesBean.isConfirmed()) { pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES_CONFIRM); return; } } try { // everything good, so lets save responses. final ResponseInfoBean responses = generateResponseInfoBean( pwmRequest, setupResponsesBean.getResponseData().getChallengeSet(), setupResponsesBean.getResponseData().getResponseMap(), setupResponsesBean.getHelpdeskResponseData().getResponseMap() ); saveResponses(pwmRequest, responses); pwmRequest.getPwmApplication().getSessionStateService().clearBean(pwmRequest, SetupResponsesBean.class); pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_SetupResponse); } catch (PwmOperationalException e) { LOGGER.error(pwmRequest.getSessionLabel(), e.getErrorInformation()); pwmRequest.respondWithError(e.getErrorInformation()); } catch (ChaiValidationException e) { final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_RANDOM_RESPONSE,e.getMessage()); LOGGER.error(pwmRequest.getSessionLabel(), errorInformation); pwmRequest.respondWithError(errorInformation); } } private void setupResponses( final PwmRequest pwmRequest, final boolean helpdeskMode ) throws PwmUnrecoverableException, IOException, ServletException, ChaiUnavailableException { final SetupResponsesBean setupResponsesBean = getSetupResponseBean(pwmRequest); final SetupResponsesBean.SetupData setupData = helpdeskMode ? setupResponsesBean.getHelpdeskResponseData() : setupResponsesBean.getResponseData(); final ChallengeSet challengeSet = setupData.getChallengeSet(); final Map<Challenge, String> responseMap; try { // build a response set based on the user's challenge set and the html form response. responseMap = readResponsesFromHttpRequest(pwmRequest, setupData); // test the responses. final int minRandomRequiredSetup = setupData.getMinRandomSetup(); pwmRequest.getPwmApplication().getCrService().validateResponses(challengeSet, responseMap, minRandomRequiredSetup); } catch (PwmDataValidationException e) { LOGGER.debug(pwmRequest, "error with new " + (helpdeskMode ? "helpdesk" : "user") + " responses: " + e.getErrorInformation().toDebugStr()); setLastError(pwmRequest, e.getErrorInformation()); return; } LOGGER.trace(pwmRequest, (helpdeskMode ? "helpdesk" : "user") + " responses are acceptable"); if (helpdeskMode) { setupResponsesBean.getHelpdeskResponseData().setResponseMap(responseMap); setupResponsesBean.setHelpdeskResponsesSatisfied(true); } else { setupResponsesBean.getResponseData().setResponseMap(responseMap); setupResponsesBean.setResponsesSatisfied(true); } } private void saveResponses(final PwmRequest pwmRequest, final ResponseInfoBean responseInfoBean) throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException, ChaiValidationException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final PwmSession pwmSession = pwmRequest.getPwmSession(); final ChaiUser theUser = pwmSession.getSessionManager().getActor(pwmApplication); final String userGUID = pwmSession.getUserInfoBean().getUserGuid(); pwmApplication.getCrService().writeResponses(pwmRequest.getUserInfoIfLoggedIn(), theUser, userGUID, responseInfoBean); final UserInfoBean uiBean = pwmSession.getUserInfoBean(); final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, pwmSession.getLabel()); userStatusReader.populateActorUserInfoBean(pwmSession, uiBean.getUserIdentity()); pwmApplication.getStatisticsManager().incrementValue(Statistic.SETUP_RESPONSES); pwmSession.getUserInfoBean().setRequiresResponseConfig(false); pwmApplication.getAuditManager().submit(AuditEvent.SET_RESPONSES, pwmSession.getUserInfoBean(), pwmSession); } private static Map<Challenge, String> readResponsesFromHttpRequest( final PwmRequest pwmRequest, final SetupResponsesBean.SetupData setupData ) throws PwmDataValidationException, PwmUnrecoverableException { final Map<String, String> inputMap = pwmRequest.readParametersAsMap(); return paramMapToChallengeMap(inputMap, setupData); } private static Map<Challenge, String> readResponsesFromJsonRequest( final PwmRequest pwmRequest, final SetupResponsesBean.SetupData setupData ) throws PwmDataValidationException, PwmUnrecoverableException, IOException { final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(); return paramMapToChallengeMap(inputMap, setupData); } private static Map<Challenge, String> paramMapToChallengeMap( final Map<String, String> inputMap, final SetupResponsesBean.SetupData setupData ) throws PwmDataValidationException, PwmUnrecoverableException { final Map<Challenge, String> readResponses = new LinkedHashMap<>(); //final SetupResponsesBean responsesBean = pwmSession.getSetupResponseBean(); { // read in the question texts and responses for (final String indexKey : setupData.getIndexedChallenges().keySet()) { final Challenge loopChallenge = setupData.getIndexedChallenges().get(indexKey); if (loopChallenge.isRequired() || !setupData.isSimpleMode()) { if (!loopChallenge.isAdminDefined()) { final String questionText = inputMap.get(PwmConstants.PARAM_QUESTION_PREFIX + indexKey); loopChallenge.setChallengeText(questionText); } final String answer = inputMap.get(PwmConstants.PARAM_RESPONSE_PREFIX + indexKey); if (answer != null && answer.length() > 0) { readResponses.put(loopChallenge, answer); } } } if (setupData.isSimpleMode()) { // if in simple mode, read the select-based random challenges for (int i = 0; i < setupData.getIndexedChallenges().size(); i++) { final String questionText = inputMap.get(PwmConstants.PARAM_QUESTION_PREFIX + "Random_" + String.valueOf(i)); Challenge challenge = null; for (final Challenge loopC : setupData.getChallengeSet().getRandomChallenges()) { if (loopC.isAdminDefined() && questionText != null && questionText.equals(loopC.getChallengeText())) { challenge = loopC; break; } } final String answer = inputMap.get(PwmConstants.PARAM_RESPONSE_PREFIX + "Random_" + String.valueOf(i)); if (answer != null && answer.length() > 0) { readResponses.put(challenge, answer); } } } } return readResponses; } private static ResponseInfoBean generateResponseInfoBean( final PwmRequest pwmRequest, final ChallengeSet challengeSet, final Map<Challenge, String> readResponses, final Map<Challenge, String> helpdeskResponses ) throws ChaiUnavailableException, PwmDataValidationException, PwmUnrecoverableException { final ChaiProvider provider = pwmRequest.getPwmSession().getSessionManager().getChaiProvider(); try { final ResponseInfoBean responseInfoBean = new ResponseInfoBean( readResponses, helpdeskResponses, challengeSet.getLocale(), challengeSet.getMinRandomRequired(), challengeSet.getIdentifier(), null, null ); final ChaiResponseSet responseSet = ChaiCrFactory.newChaiResponseSet( readResponses, challengeSet.getLocale(), challengeSet.getMinRandomRequired(), provider.getChaiConfiguration(), challengeSet.getIdentifier()); responseSet.meetsChallengeSetRequirements(challengeSet); final int minRandomRequiredSetup = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, SetupResponsesBean.class).getResponseData().getMinRandomSetup(); if (minRandomRequiredSetup == 0) { // if using recover style, then all readResponseSet must be supplied at this point. if (responseSet.getChallengeSet().getRandomChallenges().size() < challengeSet.getRandomChallenges().size()) { throw new ChaiValidationException("too few random responses", ChaiError.CR_TOO_FEW_RANDOM_RESPONSES); } } return responseInfoBean; } catch (ChaiValidationException e) { final ErrorInformation errorInfo = convertChaiValidationException(e); throw new PwmDataValidationException(errorInfo); } } private static ErrorInformation convertChaiValidationException( final ChaiValidationException e ) { switch (e.getErrorCode()) { case CR_TOO_FEW_CHALLENGES: return new ErrorInformation(PwmError.ERROR_MISSING_REQUIRED_RESPONSE, null, new String[]{e.getFieldName()}); case CR_TOO_FEW_RANDOM_RESPONSES: return new ErrorInformation(PwmError.ERROR_MISSING_RANDOM_RESPONSE, null, new String[]{e.getFieldName()}); case CR_MISSING_REQUIRED_CHALLENGE_TEXT: return new ErrorInformation(PwmError.ERROR_MISSING_CHALLENGE_TEXT, null, new String[]{e.getFieldName()}); case CR_RESPONSE_TOO_LONG: return new ErrorInformation(PwmError.ERROR_RESPONSE_TOO_LONG, null, new String[]{e.getFieldName()}); case CR_RESPONSE_TOO_SHORT: case CR_MISSING_REQUIRED_RESPONSE_TEXT: return new ErrorInformation(PwmError.ERROR_RESPONSE_TOO_SHORT, null, new String[]{e.getFieldName()}); case CR_DUPLICATE_RESPONSES: return new ErrorInformation(PwmError.ERROR_RESPONSE_DUPLICATE, null, new String[]{e.getFieldName()}); case CR_TOO_MANY_QUESTION_CHARS: return new ErrorInformation(PwmError.ERROR_CHALLENGE_IN_RESPONSE, null, new String[]{e.getFieldName()}); default: return new ErrorInformation(PwmError.ERROR_UNKNOWN); } } private void initializeBean( final PwmRequest pwmRequest, final SetupResponsesBean setupResponsesBean ) { if (pwmRequest.getPwmSession().getUserInfoBean().getResponseInfoBean() != null) { setupResponsesBean.setHasExistingResponses(true); } final ChallengeProfile challengeProfile = pwmRequest.getPwmSession().getUserInfoBean().getChallengeProfile(); if (setupResponsesBean.getResponseData() == null) { //setup user challenge data final ChallengeSet userChallengeSet = challengeProfile.getChallengeSet(); final int minRandomSetup = challengeProfile.getMinRandomSetup(); final SetupResponsesBean.SetupData userSetupData = populateSetupData(userChallengeSet,minRandomSetup); setupResponsesBean.setResponseData(userSetupData); } if (setupResponsesBean.getHelpdeskResponseData() == null) { //setup helpdesk challenge data final ChallengeSet helpdeskChallengeSet = challengeProfile.getHelpdeskChallengeSet(); if (helpdeskChallengeSet == null) { setupResponsesBean.setHelpdeskResponseData(new SetupResponsesBean.SetupData()); } else { final int minRandomHelpdeskSetup = challengeProfile.getMinHelpdeskRandomsSetup(); final SetupResponsesBean.SetupData helpdeskSetupData = populateSetupData(helpdeskChallengeSet,minRandomHelpdeskSetup); setupResponsesBean.setHelpdeskResponseData(helpdeskSetupData); } } } private static SetupResponsesBean.SetupData populateSetupData( final ChallengeSet challengeSet, final int minRandomSetup ) { boolean useSimple = true; final Map<String, Challenge> indexedChallenges = new LinkedHashMap<>(); int minRandom = minRandomSetup; { if (minRandom != 0 && minRandom < challengeSet.getMinRandomRequired()) { minRandom = challengeSet.getMinRandomRequired(); } if (minRandom > challengeSet.getRandomChallenges().size()) { minRandom = 0; } } { { if (minRandom == 0) { useSimple = false; } for (final Challenge challenge : challengeSet.getChallenges()) { if (!challenge.isRequired() && !challenge.isAdminDefined()) { useSimple = false; } } if (challengeSet.getRandomChallenges().size() == challengeSet.getMinRandomRequired()) { useSimple = false; } } } { int index = 0; for (final Challenge loopChallenge : challengeSet.getChallenges()) { indexedChallenges.put(String.valueOf(index), loopChallenge); index++; } } final SetupResponsesBean.SetupData setupData = new SetupResponsesBean.SetupData(); setupData.setChallengeSet(challengeSet); setupData.setSimpleMode(useSimple); setupData.setIndexedChallenges(indexedChallenges); setupData.setMinRandomSetup(minRandom); return setupData; } private static class ValidationResponseBean implements Serializable { private final int version = 1; private final String message; private final boolean success; private ValidationResponseBean( final String message, final boolean success ) { this.message = message; this.success = success; } public int getVersion() { return version; } public String getMessage() { return message; } public boolean isSuccess() { return success; } } }