Exemple #1
0
 private void updateCacheFromLdap()
     throws ChaiUnavailableException, ChaiOperationException, PwmOperationalException,
         PwmUnrecoverableException {
   LOGGER.debug(
       PwmConstants.REPORTING_SESSION_LABEL,
       "beginning process to updating user cache records from ldap");
   if (status != STATUS.OPEN) {
     return;
   }
   cancelFlag = false;
   reportStatus = new ReportStatusInfo(settings.getSettingsHash());
   reportStatus.setInProgress(true);
   reportStatus.setStartDate(new Date());
   try {
     final Queue<UserIdentity> allUsers = new LinkedList<>(getListOfUsers());
     reportStatus.setTotal(allUsers.size());
     while (status == STATUS.OPEN && !allUsers.isEmpty() && !cancelFlag) {
       final Date startUpdateTime = new Date();
       final UserIdentity userIdentity = allUsers.poll();
       try {
         if (updateCachedRecordFromLdap(userIdentity)) {
           reportStatus.setUpdated(reportStatus.getUpdated() + 1);
         }
       } catch (Exception e) {
         String errorMsg =
             "error while updating report cache for " + userIdentity.toString() + ", cause: ";
         errorMsg +=
             e instanceof PwmException
                 ? ((PwmException) e).getErrorInformation().toDebugStr()
                 : e.getMessage();
         final ErrorInformation errorInformation;
         errorInformation = new ErrorInformation(PwmError.ERROR_REPORTING_ERROR, errorMsg);
         LOGGER.error(PwmConstants.REPORTING_SESSION_LABEL, errorInformation.toDebugStr());
         reportStatus.setLastError(errorInformation);
         reportStatus.setErrors(reportStatus.getErrors() + 1);
       }
       reportStatus.setCount(reportStatus.getCount() + 1);
       reportStatus.getEventRateMeter().markEvents(1);
       final TimeDuration totalUpdateTime = TimeDuration.fromCurrent(startUpdateTime);
       if (settings.isAutoCalcRest()) {
         avgTracker.addSample(totalUpdateTime.getTotalMilliseconds());
         Helper.pause(avgTracker.avgAsLong());
       } else {
         Helper.pause(settings.getRestTime().getTotalMilliseconds());
       }
     }
     if (cancelFlag) {
       reportStatus.setLastError(
           new ErrorInformation(
               PwmError.ERROR_SERVICE_NOT_AVAILABLE, "report cancelled by operator"));
     }
   } finally {
     reportStatus.setFinishDate(new Date());
     reportStatus.setInProgress(false);
   }
   LOGGER.debug(
       PwmConstants.REPORTING_SESSION_LABEL,
       "update user cache process completed: " + JsonUtil.serialize(reportStatus));
 }
Exemple #2
0
 public ChaiUser getProxiedChaiUser(final UserIdentity userIdentity)
     throws PwmUnrecoverableException {
   try {
     final ChaiProvider proxiedProvider = getProxyChaiProvider(userIdentity.getLdapProfileID());
     return ChaiFactory.createChaiUser(userIdentity.getUserDN(), proxiedProvider);
   } catch (ChaiUnavailableException e) {
     throw PwmUnrecoverableException.fromChaiException(e);
   }
 }
  @Override
  void doCommand() throws Exception {
    final PwmApplication pwmApplication = cliEnvironment.getPwmApplication();

    final File outputFile =
        (File) cliEnvironment.getOptions().get(CliParameters.REQUIRED_NEW_OUTPUT_FILE.getName());
    Helper.pause(2000);

    final long startTime = System.currentTimeMillis();
    final UserSearchEngine userSearchEngine =
        new UserSearchEngine(pwmApplication, SessionLabel.SYSTEM_LABEL);
    final UserSearchEngine.SearchConfiguration searchConfiguration =
        new UserSearchEngine.SearchConfiguration();
    searchConfiguration.setEnableValueEscaping(false);
    searchConfiguration.setUsername("*");

    final String systemRecordDelimiter = System.getProperty("line.separator");
    final Writer writer =
        new BufferedWriter(new PrintWriter(outputFile, PwmConstants.DEFAULT_CHARSET.toString()));
    final Map<UserIdentity, Map<String, String>> results =
        userSearchEngine.performMultiUserSearch(
            searchConfiguration, Integer.MAX_VALUE, Collections.<String>emptyList());
    out(
        "searching "
            + results.size()
            + " users for stored responses to write to "
            + outputFile.getAbsolutePath()
            + "....");
    int counter = 0;
    for (final UserIdentity identity : results.keySet()) {
      final ChaiUser user = pwmApplication.getProxiedChaiUser(identity);
      final ResponseSet responseSet =
          pwmApplication.getCrService().readUserResponseSet(null, identity, user);
      if (responseSet != null) {
        counter++;
        out("found responses for '" + user + "', writing to output.");
        final RestChallengesServer.JsonChallengesData outputData =
            new RestChallengesServer.JsonChallengesData();
        outputData.challenges = responseSet.asChallengeBeans(true);
        outputData.helpdeskChallenges = responseSet.asHelpdeskChallengeBeans(true);
        outputData.minimumRandoms = responseSet.getChallengeSet().minimumResponses();
        outputData.username = identity.toDelimitedKey();
        writer.write(JsonUtil.serialize(outputData));
        writer.write(systemRecordDelimiter);
      } else {
        out("skipping '" + user.toString() + "', no stored responses.");
      }
    }
    writer.close();
    out(
        "output complete, "
            + counter
            + " responses exported in "
            + TimeDuration.fromCurrent(startTime).asCompactString());
  }
  private PasswordData setTempUserPassword()
      throws ChaiUnavailableException, ImpossiblePasswordPolicyException,
          PwmUnrecoverableException {

    final boolean configAlwaysUseProxy =
        pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);

    final ChaiProvider chaiProvider =
        pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
    final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);

    // try setting a random password on the account to authenticate.
    if (!configAlwaysUseProxy && requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
      log(PwmLogLevel.DEBUG, "attempting to set temporary random password");

      PwmPasswordPolicy passwordPolicy =
          PasswordUtility.readPasswordPolicyForUser(
              pwmApplication, sessionLabel, userIdentity, chaiUser, PwmConstants.DEFAULT_LOCALE);

      // create random password for user
      RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig =
          new RandomPasswordGenerator.RandomGeneratorConfig();
      randomGeneratorConfig.setSeedlistPhrases(RandomPasswordGenerator.DEFAULT_SEED_PHRASES);
      randomGeneratorConfig.setPasswordPolicy(passwordPolicy);

      final PasswordData currentPass =
          RandomPasswordGenerator.createRandomPassword(
              sessionLabel, randomGeneratorConfig, pwmApplication);

      try {
        final String oracleDS_PrePasswordAllowChangeTime =
            oraclePreTemporaryPwHandler(chaiProvider, chaiUser);

        // write the random password for the user.
        chaiUser.setPassword(currentPass.getStringValue());

        oraclePostTemporaryPwHandler(chaiProvider, chaiUser, oracleDS_PrePasswordAllowChangeTime);

        log(
            PwmLogLevel.INFO,
            "user "
                + userIdentity
                + " password has been set to random value to use for user authentication");
      } catch (ChaiOperationException e) {
        final String errorStr =
            "error setting random password for user " + userIdentity + " " + e.getMessage();
        log(PwmLogLevel.ERROR, errorStr);
        throw new PwmUnrecoverableException(
            new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
      }

      return currentPass;
    }
    return null;
  }
 private ChaiProvider makeProxyProvider()
     throws ChaiUnavailableException, PwmUnrecoverableException {
   final LdapProfile profile =
       pwmApplication.getConfig().getLdapProfiles().get(userIdentity.getLdapProfileID());
   final String proxyDN = profile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
   final PasswordData proxyPassword =
       profile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
   return LdapOperationsHelper.createChaiProvider(
       sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword);
 }
  private PasswordData learnUserPassword()
      throws ChaiUnavailableException, PwmUnrecoverableException {
    log(PwmLogLevel.TRACE, "beginning auth processes for user with unknown password");

    if (userIdentity == null
        || userIdentity.getUserDN() == null
        || userIdentity.getUserDN().length() < 1) {
      throw new NullPointerException("invalid user (null)");
    }

    final ChaiProvider chaiProvider =
        pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
    final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);

    // use chai (nmas) to retrieve user password
    if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_USER_PWD)) {
      String currentPass = null;
      try {
        final String readPassword = chaiUser.readPassword();
        if (readPassword != null && readPassword.length() > 0) {
          currentPass = readPassword;
          log(
              PwmLogLevel.DEBUG,
              "successfully retrieved user's current password from ldap, now conducting standard authentication");
        }
      } catch (Exception e) {
        log(PwmLogLevel.ERROR, "unable to retrieve user password from ldap: " + e.getMessage());
      }

      // actually do the authentication since we have user pw.
      if (currentPass != null && currentPass.length() > 0) {
        return new PasswordData(currentPass);
      }
    } else {
      log(PwmLogLevel.TRACE, "skipping attempt to read user password, option disabled");
    }
    return null;
  }
Exemple #7
0
  private static Date determinePwdLastModified(
      final PwmApplication pwmApplication,
      final SessionLabel sessionLabel,
      final ChaiUser theUser,
      final UserIdentity userIdentity)
      throws ChaiUnavailableException, PwmUnrecoverableException {
    // fetch last password modification time from pwm last update attribute operation
    try {
      final Date chaiReadDate = theUser.readPasswordModificationDate();
      if (chaiReadDate != null) {
        LOGGER.trace(
            sessionLabel,
            "read last user password change timestamp (via chai) as: "
                + PwmConstants.DEFAULT_DATETIME_FORMAT.format(chaiReadDate));
        return chaiReadDate;
      }
    } catch (ChaiOperationException e) {
      LOGGER.error(
          sessionLabel,
          "unexpected error reading password last modified timestamp: " + e.getMessage());
    }

    final LdapProfile ldapProfile =
        pwmApplication.getConfig().getLdapProfiles().get(userIdentity.getLdapProfileID());
    final String pwmLastSetAttr =
        ldapProfile.readSettingAsString(PwmSetting.PASSWORD_LAST_UPDATE_ATTRIBUTE);
    if (pwmLastSetAttr != null && pwmLastSetAttr.length() > 0) {
      try {
        final Date pwmPwdLastModified = theUser.readDateAttribute(pwmLastSetAttr);
        LOGGER.trace(
            sessionLabel,
            "read pwmPasswordChangeTime as: "
                + (pwmPwdLastModified == null
                    ? "n/a"
                    : PwmConstants.DEFAULT_DATETIME_FORMAT.format(pwmPwdLastModified)));
        return pwmPwdLastModified;
      } catch (ChaiOperationException e) {
        LOGGER.error(
            sessionLabel,
            "error parsing password last modified PWM password value for user "
                + theUser.getEntryDN()
                + "; error: "
                + e.getMessage());
      }
    }

    LOGGER.debug(sessionLabel, "unable to determine time of user's last password modification");
    return null;
  }
 private static void updateLoginHistory(
     final PwmRequest pwmRequest, final UserIdentity userIdentity, boolean successful) {
   final ConfigLoginHistory configLoginHistory = readConfigLoginHistory(pwmRequest);
   final ConfigLoginEvent event =
       new ConfigLoginEvent(
           userIdentity == null ? "n/a" : userIdentity.toDisplayString(),
           new Date(),
           pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress());
   final int maxEvents =
       Integer.parseInt(
           pwmRequest
               .getPwmApplication()
               .getConfig()
               .readAppProperty(AppProperty.CONFIG_HISTORY_MAX_ITEMS));
   configLoginHistory.addEvent(event, maxEvents, successful);
   pwmRequest
       .getPwmApplication()
       .writeAppAttribute(PwmApplication.AppAttribute.CONFIG_LOGIN_HISTORY, configLoginHistory);
 }
Exemple #9
0
 public static Map<String, Date> readIndividualReplicaLastPasswordTimes(
     final PwmApplication pwmApplication,
     final SessionLabel sessionLabel,
     final UserIdentity userIdentity)
     throws PwmUnrecoverableException {
   final Map<String, Date> returnValue = new LinkedHashMap<>();
   final ChaiProvider chaiProvider =
       pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
   final Collection<ChaiConfiguration> perReplicaConfigs =
       ChaiUtility.splitConfigurationPerReplica(
           chaiProvider.getChaiConfiguration(),
           Collections.singletonMap(ChaiSetting.FAILOVER_CONNECT_RETRIES, "1"));
   for (final ChaiConfiguration loopConfiguration : perReplicaConfigs) {
     final String loopReplicaUrl = loopConfiguration.getSetting(ChaiSetting.BIND_DN);
     ChaiProvider loopProvider = null;
     try {
       loopProvider = ChaiProviderFactory.createProvider(loopConfiguration);
       final Date lastModifiedDate =
           determinePwdLastModified(pwmApplication, sessionLabel, userIdentity);
       returnValue.put(loopReplicaUrl, lastModifiedDate);
     } catch (ChaiUnavailableException e) {
       LOGGER.error(sessionLabel, "unreachable server during replica password sync check");
       e.printStackTrace();
     } finally {
       if (loopProvider != null) {
         try {
           loopProvider.close();
         } catch (Exception e) {
           final String errorMsg =
               "error closing loopProvider to "
                   + loopReplicaUrl
                   + " while checking individual password sync status";
           LOGGER.error(sessionLabel, errorMsg);
         }
       }
     }
   }
   return returnValue;
 }
  @Override
  void doCommand() throws Exception {
    final PwmApplication pwmApplication = cliEnvironment.getPwmApplication();

    final File inputFile =
        (File)
            cliEnvironment.getOptions().get(CliParameters.REQUIRED_EXISTING_INPUT_FILE.getName());
    final BufferedReader reader =
        new BufferedReader(
            new InputStreamReader(
                new FileInputStream(inputFile), PwmConstants.DEFAULT_CHARSET.toString()));
    out("importing stored responses from " + inputFile.getAbsolutePath() + "....");

    int counter = 0;
    String line;
    final long startTime = System.currentTimeMillis();
    while ((line = reader.readLine()) != null) {
      counter++;
      final RestChallengesServer.JsonChallengesData inputData;
      inputData = JsonUtil.deserialize(line, RestChallengesServer.JsonChallengesData.class);

      final UserIdentity userIdentity = UserIdentity.fromDelimitedKey(inputData.username);
      final ChaiUser user = pwmApplication.getProxiedChaiUser(userIdentity);
      if (user.isValid()) {
        out("writing responses to user '" + user.getEntryDN() + "'");
        try {
          final ChallengeProfile challengeProfile =
              pwmApplication
                  .getCrService()
                  .readUserChallengeProfile(
                      null,
                      userIdentity,
                      user,
                      PwmPasswordPolicy.defaultPolicy(),
                      PwmConstants.DEFAULT_LOCALE);
          final ChallengeSet challengeSet = challengeProfile.getChallengeSet();
          final String userGuid =
              LdapOperationsHelper.readLdapGuidValue(pwmApplication, null, userIdentity, false);
          final ResponseInfoBean responseInfoBean =
              inputData.toResponseInfoBean(
                  PwmConstants.DEFAULT_LOCALE, challengeSet.getIdentifier());
          pwmApplication.getCrService().writeResponses(user, userGuid, responseInfoBean);
        } catch (Exception e) {
          out(
              "error writing responses to user '"
                  + user.getEntryDN()
                  + "', error: "
                  + e.getMessage());
          return;
        }
      } else {
        out("user '" + user.getEntryDN() + "' is not a valid userDN");
        return;
      }
    }

    out(
        "output complete, "
            + counter
            + " responses imported in "
            + TimeDuration.fromCurrent(startTime).asCompactString());
  }
Exemple #11
0
  private boolean updateCachedRecordFromLdap(
      final UserIdentity userIdentity,
      final UserInfoBean userInfoBean,
      final UserCacheService.StorageKey storageKey)
      throws ChaiUnavailableException, PwmUnrecoverableException, LocalDBException {
    final UserCacheRecord userCacheRecord = userCacheService.readStorageKey(storageKey);
    TimeDuration cacheAge = null;
    if (userCacheRecord != null && userCacheRecord.getCacheTimestamp() != null) {
      cacheAge = TimeDuration.fromCurrent(userCacheRecord.getCacheTimestamp());
    }

    boolean updateCache = false;
    if (userInfoBean != null) {
      updateCache = true;
    } else {
      if (cacheAge == null) {
        LOGGER.trace(
            PwmConstants.REPORTING_SESSION_LABEL,
            "stored cache for "
                + userIdentity
                + " is missing cache storage timestamp, will update");
        updateCache = true;
      } else if (cacheAge.isLongerThan(settings.getMinCacheAge())) {
        LOGGER.trace(
            PwmConstants.REPORTING_SESSION_LABEL,
            "stored cache for "
                + userIdentity
                + " is "
                + cacheAge.asCompactString()
                + " old, will update");
        updateCache = true;
      }
    }

    if (updateCache) {
      if (userCacheRecord != null) {
        if (summaryData != null
            && summaryData.getEpoch() != null
            && summaryData.getEpoch().equals(userCacheRecord.getSummaryEpoch())) {
          summaryData.remove(userCacheRecord);
        }
      }
      final UserInfoBean newUserBean;
      if (userInfoBean != null) {
        newUserBean = userInfoBean;
      } else {
        newUserBean = new UserInfoBean();
        final UserStatusReader.Settings readerSettings = new UserStatusReader.Settings();
        readerSettings.setSkipReportUpdate(true);
        final ChaiProvider chaiProvider =
            pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
        final UserStatusReader userStatusReader =
            new UserStatusReader(
                pwmApplication, PwmConstants.REPORTING_SESSION_LABEL, readerSettings);
        userStatusReader.populateUserInfoBean(
            newUserBean, PwmConstants.DEFAULT_LOCALE, userIdentity, chaiProvider);
      }
      final UserCacheRecord newUserCacheRecord = userCacheService.updateUserCache(newUserBean);

      if (summaryData != null && summaryData.getEpoch() != null && newUserCacheRecord != null) {
        if (!summaryData.getEpoch().equals(newUserCacheRecord.getSummaryEpoch())) {
          newUserCacheRecord.setSummaryEpoch(summaryData.getEpoch());
          userCacheService.store(newUserCacheRecord);
        }
        summaryData.update(newUserCacheRecord);
      }
    }

    return updateCache;
  }
Exemple #12
0
  public static void helpdeskSetUserPassword(
      final PwmSession pwmSession,
      final ChaiUser chaiUser,
      final UserIdentity userIdentity,
      final PwmApplication pwmApplication,
      final PasswordData newPassword)
      throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException {
    final SessionLabel sessionLabel = pwmSession.getLabel();

    if (!pwmSession.isAuthenticated()) {
      final String errorMsg = "attempt to helpdeskSetUserPassword, but user is not authenticated";
      final ErrorInformation errorInformation =
          new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, errorMsg);
      throw new PwmOperationalException(errorInformation);
    }

    final HelpdeskProfile helpdeskProfile =
        pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
    if (helpdeskProfile == null) {
      final String errorMsg =
          "attempt to helpdeskSetUserPassword, but user does not have helpdesk permission";
      final ErrorInformation errorInformation =
          new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, errorMsg);
      throw new PwmOperationalException(errorInformation);
    }

    try {
      chaiUser.setPassword(newPassword.getStringValue());
    } catch (ChaiPasswordPolicyException e) {
      final String errorMsg =
          "error setting password for user '" + chaiUser.getEntryDN() + "'' " + e.toString();
      final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
      final ErrorInformation error =
          new ErrorInformation(
              pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError, errorMsg);
      throw new PwmOperationalException(error);
    } catch (ChaiOperationException e) {
      final String errorMsg =
          "error setting password for user '" + chaiUser.getEntryDN() + "'' " + e.getMessage();
      final PwmError pwmError =
          PwmError.forChaiError(e.getErrorCode()) == null
              ? PwmError.ERROR_UNKNOWN
              : PwmError.forChaiError(e.getErrorCode());
      final ErrorInformation error = new ErrorInformation(pwmError, errorMsg);
      throw new PwmOperationalException(error);
    }

    // at this point the password has been changed, so log it.
    LOGGER.info(
        sessionLabel,
        "user '"
            + pwmSession.getUserInfoBean().getUserIdentity()
            + "' successfully changed password for "
            + chaiUser.getEntryDN());

    // create a proxy user object for pwm to update/read the user.
    final ChaiUser proxiedUser = pwmApplication.getProxiedChaiUser(userIdentity);

    // mark the event log
    {
      final HelpdeskAuditRecord auditRecord =
          pwmApplication
              .getAuditManager()
              .createHelpdeskAuditRecord(
                  AuditEvent.HELPDESK_SET_PASSWORD,
                  pwmSession.getUserInfoBean().getUserIdentity(),
                  null,
                  userIdentity,
                  pwmSession.getSessionStateBean().getSrcAddress(),
                  pwmSession.getSessionStateBean().getSrcHostname());
      pwmApplication.getAuditManager().submit(auditRecord);
    }

    // update statistics
    pwmApplication.getStatisticsManager().updateEps(Statistic.EpsType.PASSWORD_CHANGES, 1);
    pwmApplication.getStatisticsManager().incrementValue(Statistic.HELPDESK_PASSWORD_SET);

    // create a uib for end user
    final UserInfoBean userInfoBean = new UserInfoBean();
    final UserStatusReader userStatusReader =
        new UserStatusReader(pwmApplication, pwmSession.getLabel());
    userStatusReader.populateUserInfoBean(
        userInfoBean,
        pwmSession.getSessionStateBean().getLocale(),
        userIdentity,
        proxiedUser.getChaiProvider());

    { // execute configured actions
      LOGGER.debug(
          sessionLabel,
          "executing changepassword and helpdesk post password change writeAttributes to user "
              + userIdentity);
      final List<ActionConfiguration> actions = new ArrayList<>();
      actions.addAll(
          pwmApplication
              .getConfig()
              .readSettingAsAction(PwmSetting.CHANGE_PASSWORD_WRITE_ATTRIBUTES));
      actions.addAll(
          helpdeskProfile.readSettingAsAction(
              PwmSetting.HELPDESK_POST_SET_PASSWORD_WRITE_ATTRIBUTES));
      if (!actions.isEmpty()) {

        final ActionExecutor actionExecutor =
            new ActionExecutor.ActionExecutorSettings(pwmApplication, userIdentity)
                .setMacroMachine(
                    MacroMachine.forUser(
                        pwmApplication,
                        pwmSession.getSessionStateBean().getLocale(),
                        sessionLabel,
                        userIdentity))
                .setExpandPwmMacros(true)
                .createActionExecutor();

        actionExecutor.executeActions(actions, pwmSession);
      }
    }

    final HelpdeskClearResponseMode settingClearResponses =
        HelpdeskClearResponseMode.valueOf(
            helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_CLEAR_RESPONSES));
    if (settingClearResponses == HelpdeskClearResponseMode.yes) {
      final String userGUID =
          LdapOperationsHelper.readLdapGuidValue(pwmApplication, sessionLabel, userIdentity, false);
      pwmApplication.getCrService().clearResponses(pwmSession, proxiedUser, userGUID);

      // mark the event log
      final HelpdeskAuditRecord auditRecord =
          pwmApplication
              .getAuditManager()
              .createHelpdeskAuditRecord(
                  AuditEvent.HELPDESK_CLEAR_RESPONSES,
                  pwmSession.getUserInfoBean().getUserIdentity(),
                  null,
                  userIdentity,
                  pwmSession.getSessionStateBean().getSrcAddress(),
                  pwmSession.getSessionStateBean().getSrcHostname());
      pwmApplication.getAuditManager().submit(auditRecord);
    }

    // send email notification
    sendChangePasswordHelpdeskEmailNotice(pwmSession, pwmApplication, userInfoBean);

    // expire if so configured
    if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_FORCE_PW_EXPIRATION)) {
      LOGGER.trace(
          pwmSession, "preparing to expire password for user " + userIdentity.toDisplayString());
      try {
        proxiedUser.expirePassword();
      } catch (ChaiOperationException e) {
        LOGGER.warn(
            pwmSession,
            "error while forcing password expiration for user "
                + userIdentity.toDisplayString()
                + ", error: "
                + e.getMessage());
        e.printStackTrace();
      }
    }

    // send password
    final boolean sendPassword =
        helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_SEND_PASSWORD);
    if (sendPassword) {
      final MessageSendMethod messageSendMethod;
      {
        final String profileID =
            ProfileUtility.discoverProfileIDforUser(
                pwmApplication, sessionLabel, userIdentity, ProfileType.ForgottenPassword);
        final ForgottenPasswordProfile forgottenPasswordProfile =
            pwmApplication.getConfig().getForgottenPasswordProfiles().get(profileID);
        messageSendMethod =
            forgottenPasswordProfile.readSettingAsEnum(
                PwmSetting.RECOVERY_SENDNEWPW_METHOD, MessageSendMethod.class);
      }
      final UserDataReader userDataReader = new LdapUserDataReader(userIdentity, chaiUser);
      final LoginInfoBean loginInfoBean = new LoginInfoBean();
      loginInfoBean.setUserCurrentPassword(newPassword);
      final MacroMachine macroMachine =
          new MacroMachine(
              pwmApplication, pwmSession.getLabel(), userInfoBean, loginInfoBean, userDataReader);
      PasswordUtility.sendNewPassword(
          userInfoBean,
          pwmApplication,
          macroMachine,
          newPassword,
          pwmSession.getSessionStateBean().getLocale(),
          messageSendMethod);
    }
  }
  private void testCredentials(final UserIdentity userIdentity, final PasswordData password)
      throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException {
    log(PwmLogLevel.TRACE, "beginning testCredentials process");

    if (userIdentity == null
        || userIdentity.getUserDN() == null
        || userIdentity.getUserDN().length() < 1) {
      final String errorMsg = "attempt to authenticate with null userDN";
      log(PwmLogLevel.DEBUG, errorMsg);
      throw new PwmOperationalException(
          new ErrorInformation(PwmError.ERROR_WRONGPASSWORD, errorMsg));
    }

    if (password == null) {
      final String errorMsg = "attempt to authenticate with null password";
      log(PwmLogLevel.DEBUG, errorMsg);
      throw new PwmOperationalException(
          new ErrorInformation(PwmError.ERROR_WRONGPASSWORD, errorMsg));
    }

    // try authenticating the user using a normal ldap BIND operation.
    log(PwmLogLevel.TRACE, "attempting authentication using ldap BIND");

    boolean bindSucceeded = false;
    try {
      // read a provider using the user's DN and password.
      userProvider =
          LdapOperationsHelper.createChaiProvider(
              sessionLabel,
              userIdentity.getLdapProfile(pwmApplication.getConfig()),
              pwmApplication.getConfig(),
              userIdentity.getUserDN(),
              password);

      // issue a read operation to trigger a bind.
      userProvider.readStringAttribute(
          userIdentity.getUserDN(), ChaiConstant.ATTR_LDAP_OBJECTCLASS);

      bindSucceeded = true;
    } catch (ChaiException e) {
      if (e.getErrorCode() != null && e.getErrorCode() == ChaiError.INTRUDER_LOCKOUT) {
        final String errorMsg =
            "intruder lockout detected for user "
                + userIdentity
                + " marking session as locked out: "
                + e.getMessage();
        final ErrorInformation errorInformation =
            new ErrorInformation(PwmError.ERROR_INTRUDER_LDAP, errorMsg);
        log(PwmLogLevel.WARN, errorInformation.toDebugStr());
        throw new PwmUnrecoverableException(errorInformation);
      }
      final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
      final ErrorInformation errorInformation;
      if (pwmError != null && PwmError.ERROR_UNKNOWN != pwmError) {
        errorInformation = new ErrorInformation(pwmError, e.getMessage());
      } else {
        errorInformation =
            new ErrorInformation(
                PwmError.ERROR_WRONGPASSWORD,
                "ldap error during password check: " + e.getMessage());
      }
      log(PwmLogLevel.DEBUG, errorInformation.toDebugStr());
      throw new PwmOperationalException(errorInformation);
    } finally {
      if (!bindSucceeded && userProvider != null) {
        try {
          userProvider.close();
          userProvider = null;
        } catch (Throwable e) {
          log(
              PwmLogLevel.ERROR,
              "unexpected error closing invalid ldap connection after failed login attempt: "
                  + e.getMessage());
        }
      }
    }
  }
  private AuthenticationResult authenticateUserImpl(final PasswordData password)
      throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException {
    if (startTime == null) {
      startTime = new Date();
    }

    log(
        PwmLogLevel.DEBUG,
        "preparing to authenticate user using authenticationType="
            + this.requestedAuthType
            + " using strategy "
            + this.strategy);

    final StatisticsManager statisticsManager = pwmApplication.getStatisticsManager();
    final IntruderManager intruderManager = pwmApplication.getIntruderManager();
    intruderManager.convenience().checkUserIdentity(userIdentity);
    intruderManager.check(RecordType.ADDRESS, sessionLabel.getSrcAddress());

    boolean allowBindAsUser = true;
    if (strategy == AuthenticationStrategy.ADMIN_PROXY) {
      allowBindAsUser = false;
    }

    if (allowBindAsUser) {
      try {
        testCredentials(userIdentity, password);
      } catch (PwmOperationalException e) {
        boolean permitAuthDespiteError = false;
        final ChaiProvider.DIRECTORY_VENDOR vendor =
            pwmApplication
                .getProxyChaiProvider(userIdentity.getLdapProfileID())
                .getDirectoryVendor();
        if (PwmError.PASSWORD_NEW_PASSWORD_REQUIRED == e.getError()) {
          if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
            if (pwmApplication
                .getConfig()
                .readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
              log(
                  PwmLogLevel.INFO,
                  "auth bind failed, but will allow login due to 'must change password on next login AD error', error: "
                      + e.getErrorInformation().toDebugStr());
              allowBindAsUser = false;
              permitAuthDespiteError = true;
            }
          } else if (vendor == ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS) {
            if (pwmApplication
                .getConfig()
                .readSettingAsBoolean(PwmSetting.ORACLE_DS_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
              log(
                  PwmLogLevel.INFO,
                  "auth bind failed, but will allow login due to 'pwdReset' user attribute, error: "
                      + e.getErrorInformation().toDebugStr());
              allowBindAsUser = false;
              permitAuthDespiteError = true;
            }
          }
        } else if (PwmError.PASSWORD_EXPIRED
            == e.getError()) { // handle ad case where password is expired
          if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
            if (pwmApplication
                .getConfig()
                .readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
              if (!pwmApplication
                  .getConfig()
                  .readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_EXPIRED)) {
                throw e;
              }
              log(
                  PwmLogLevel.INFO,
                  "auth bind failed, but will allow login due to 'password expired AD error', error: "
                      + e.getErrorInformation().toDebugStr());
              allowBindAsUser = false;
              permitAuthDespiteError = true;
            }
          }
        }

        if (!permitAuthDespiteError) { // auth failed, presumably due to wrong password.
          statisticsManager.incrementValue(Statistic.AUTHENTICATION_FAILURES);
          throw e;
        }
      }
    } else {
      // verify user is not account disabled
      AuthenticationUtility.checkIfUserEligibleToAuthentication(pwmApplication, userIdentity);
    }

    statisticsManager.incrementValue(Statistic.AUTHENTICATIONS);
    statisticsManager.updateEps(Statistic.EpsType.AUTHENTICATION, 1);
    statisticsManager.updateAverageValue(
        Statistic.AVG_AUTHENTICATION_TIME,
        TimeDuration.fromCurrent(startTime).getTotalMilliseconds());

    final AuthenticationType returnAuthType;
    if (!allowBindAsUser) {
      returnAuthType = AuthenticationType.AUTH_BIND_INHIBIT;
    } else {
      if (requestedAuthType == null) {
        returnAuthType = AuthenticationType.AUTHENTICATED;
      } else {
        if (requestedAuthType == AuthenticationType.AUTH_WITHOUT_PASSWORD) {
          returnAuthType = AuthenticationType.AUTHENTICATED;
        } else if (requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
          returnAuthType = AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
        } else {
          returnAuthType = requestedAuthType;
        }
      }
    }

    final boolean useProxy = determineIfLdapProxyNeeded(returnAuthType, password);
    final ChaiProvider returnProvider = useProxy ? makeProxyProvider() : userProvider;
    final AuthenticationResult authenticationResult =
        new AuthenticationResult(returnProvider, returnAuthType, password);

    final StringBuilder debugMsg = new StringBuilder();
    debugMsg.append("successful ldap authentication for ").append(userIdentity);
    debugMsg.append(" (").append(TimeDuration.fromCurrent(startTime).asCompactString()).append(")");
    debugMsg.append(" type: ").append(returnAuthType).append(", using strategy ").append(strategy);
    debugMsg.append(", using proxy connection: ").append(useProxy);
    debugMsg
        .append(", returning bind dn: ")
        .append(
            returnProvider == null
                ? "none"
                : returnProvider.getChaiConfiguration().getSetting(ChaiSetting.BIND_DN));
    log(PwmLogLevel.INFO, debugMsg);
    pwmApplication
        .getAuditManager()
        .submit(
            pwmApplication
                .getAuditManager()
                .createUserAuditRecord(
                    AuditEvent.AUTHENTICATE,
                    this.userIdentity,
                    makeAuditLogMessage(returnAuthType),
                    sessionLabel.getSrcAddress(),
                    sessionLabel.getSrcHostname()));

    return authenticationResult;
  }