mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Ludovic Poitou
17.41.2015 3be59b3c0b3a13204a84aad9e391ec7914eea206
Fix for OpenDJ-1610. When changing a password using the password modify extended operation to let the server generate the new password, the current password was not set in the history.
Added unit test, and cleanup the whole test suite.
2 files modified
1871 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java 487 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java 1384 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -70,37 +70,23 @@
 * as for generating a new password if none was provided.
 */
public class PasswordModifyExtendedOperation
       extends ExtendedOperationHandler<
                    PasswordModifyExtendedOperationHandlerCfg>
       implements ConfigurationChangeListener<
                    PasswordModifyExtendedOperationHandlerCfg>
       extends ExtendedOperationHandler<PasswordModifyExtendedOperationHandlerCfg>
       implements ConfigurationChangeListener<PasswordModifyExtendedOperationHandlerCfg>
{
  // The following attachments may be used by post-op plugins (e.g. Samba) in
  // order to avoid re-decoding the request parameters and also to enforce
  // atomicity.
  /**
   * The name of the attachment which will be used to store the fully resolved
   * target entry.
   */
  /** The name of the attachment which will be used to store the fully resolved target entry. */
  public static final String AUTHZ_DN_ATTACHMENT;
  /**
   * The name of the attachment which will be used to store the password
   * attribute.
   */
  /** The name of the attachment which will be used to store the password attribute. */
  public static final String PWD_ATTRIBUTE_ATTACHMENT;
  /**
   * The clear text password, which may not be present if the provided password
   * was pre-encoded.
   */
  /** The clear text password, which may not be present if the provided password was pre-encoded. */
  public static final String CLEAR_PWD_ATTACHMENT;
  /**
   * A list containing the encoded passwords: plugins can perform changes
   * atomically via CAS.
   */
  /** A list containing the encoded passwords: plugins can perform changes atomically via CAS. */
  public static final String ENCODED_PWD_ATTACHMENT;
  static
@@ -122,22 +108,21 @@
  /** The reference to the identity mapper. */
  private IdentityMapper<?> identityMapper;
  /**
   * Create an instance of this password modify extended operation.  All
   * initialization should be performed in the
   * Create an instance of this password modify extended operation.  All initialization should be performed in the
   * <CODE>initializeExtendedOperationHandler</CODE> method.
   */
  public PasswordModifyExtendedOperation()
  {
    super(new HashSet<String>(Arrays.asList(
        OID_LDAP_NOOP_OPENLDAP_ASSIGNED, OID_PASSWORD_POLICY_CONTROL)));
    super(new HashSet<String>(Arrays.asList(OID_LDAP_NOOP_OPENLDAP_ASSIGNED, OID_PASSWORD_POLICY_CONTROL)));
  }
  /**
   * Initializes this extended operation handler based on the information in the
   * provided configuration.  It should also register itself with the
   * Directory Server for the particular kinds of extended operations that it
   * will process.
   * Initializes this extended operation handler based on the information in the provided configuration.
   * It should also register itself with the Directory Server for the particular kinds of extended operations
   * that it will process.
   *
   * @param   config      The configuration that contains the information
   *                      to use to initialize this extended operation handler.
@@ -146,12 +131,10 @@
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   *                                   that is not related to the server configuration.
   */
  @Override
  public void initializeExtendedOperationHandler(
       PasswordModifyExtendedOperationHandlerCfg config)
  public void initializeExtendedOperationHandler(PasswordModifyExtendedOperationHandlerCfg config)
         throws ConfigException, InitializationException
  {
    try
@@ -167,8 +150,8 @@
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message = ERR_EXTOP_PASSMOD_CANNOT_DETERMINE_ID_MAPPER.get(
          config.dn(), getExceptionMessage(e));
      LocalizableMessage message = ERR_EXTOP_PASSMOD_CANNOT_DETERMINE_ID_MAPPER
          .get(config.dn(), getExceptionMessage(e));
      throw new InitializationException(message, e);
    }
@@ -182,10 +165,9 @@
  }
  /**
   * Performs any finalization that may be necessary for this extended
   * operation handler.  By default, no finalization is performed.
   * Performs any finalization that may be necessary for this extended operation handler.
   * By default, no finalization is performed.
   */
  @Override
  public void finalizeExtendedOperationHandler()
@@ -195,6 +177,7 @@
    super.finalizeExtendedOperationHandler();
  }
  /**
   * Processes the provided extended operation.
   *
@@ -203,13 +186,11 @@
  @Override
  public void processExtendedOperation(ExtendedOperation operation)
  {
    // Initialize the variables associated with components that may be included
    // in the request.
    // Initialize the variables associated with components that may be included in the request.
    ByteString userIdentity = null;
    ByteString oldPassword  = null;
    ByteString newPassword  = null;
    // Look at the set of controls included in the request, if there are any.
    boolean                   noOpRequested        = false;
    boolean                   pwPolicyRequested    = false;
@@ -233,7 +214,6 @@
      }
    }
    // Parse the encoded request, if there is one.
    ByteString requestValue = operation.getRequestValue();
    if (requestValue != null)
@@ -242,18 +222,15 @@
      {
        ASN1Reader reader = ASN1.getReader(requestValue);
        reader.readStartSequence();
        if(reader.hasNextElement() &&
            reader.peekType() == TYPE_PASSWORD_MODIFY_USER_ID)
        if(reader.hasNextElement() && reader.peekType() == TYPE_PASSWORD_MODIFY_USER_ID)
        {
          userIdentity = reader.readOctetString();
        }
        if(reader.hasNextElement() &&
            reader.peekType() == TYPE_PASSWORD_MODIFY_OLD_PASSWORD)
        if(reader.hasNextElement() && reader.peekType() == TYPE_PASSWORD_MODIFY_OLD_PASSWORD)
        {
          oldPassword = reader.readOctetString();
        }
        if(reader.hasNextElement() &&
            reader.peekType() == TYPE_PASSWORD_MODIFY_NEW_PASSWORD)
        if(reader.hasNextElement() && reader.peekType() == TYPE_PASSWORD_MODIFY_NEW_PASSWORD)
        {
          newPassword = reader.readOctetString();
        }
@@ -264,17 +241,14 @@
        logger.traceException(ae);
        operation.setResultCode(ResultCode.PROTOCOL_ERROR);
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_DECODE_REQUEST
            .get(getExceptionMessage(ae)));
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_DECODE_REQUEST.get(getExceptionMessage(ae)));
        return;
      }
    }
    // Get the entry for the user that issued the request.
    Entry requestorEntry = operation.getAuthorizationEntry();
    // See if a user identity was provided.  If so, then try to resolve it to
    // an actual user.
    DN    userDN    = null;
@@ -285,23 +259,19 @@
    {
      if (userIdentity == null)
      {
        // This request must be targeted at changing the password for the
        // currently-authenticated user.  Make sure that the user actually is
        // authenticated.
        // This request must be targeted at changing the password for the currently-authenticated user.
        // Make sure that the user actually is authenticated.
        ClientConnection   clientConnection = operation.getClientConnection();
        AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
        if ((! authInfo.isAuthenticated()) || (requestorEntry == null))
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
          operation.appendErrorMessage(
                  ERR_EXTOP_PASSMOD_NO_AUTH_OR_USERID.get());
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_NO_AUTH_OR_USERID.get());
          return;
        }
        // Retrieve a write lock on that user's entry.
        userDN = requestorEntry.getName();
        userLock = LockManager.lockWrite(userDN);
        if (userLock == null)
        {
@@ -328,13 +298,11 @@
            logger.traceException(de);
            operation.setResultCode(ResultCode.INVALID_DN_SYNTAX);
            operation.appendErrorMessage(
                    ERR_EXTOP_PASSMOD_CANNOT_DECODE_AUTHZ_DN.get(authzIDStr));
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_DECODE_AUTHZ_DN.get(authzIDStr));
            return;
          }
          // If the provided DN is an alternate DN for a root user, then replace
          // it with the actual root DN.
          // If the provided DN is an alternate DN for a root user, then replace it with the actual root DN.
          DN actualRootDN = DirectoryServer.getActualRootBindDN(userDN);
          if (actualRootDN != null)
          {
@@ -355,8 +323,7 @@
            if (userEntry == null)
            {
              operation.setResultCode(ResultCode.NO_SUCH_OBJECT);
              operation.appendErrorMessage(
                      ERR_EXTOP_PASSMOD_CANNOT_MAP_USER.get(authzIDStr));
              operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_MAP_USER.get(authzIDStr));
              return;
            }
@@ -368,15 +335,13 @@
            //Encountered an exception while resolving identity.
            operation.setResultCode(de.getResultCode());
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_ERROR_MAPPING_USER
                    .get(authzIDStr,de.getMessageObject()));
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_ERROR_MAPPING_USER.get(authzIDStr, de.getMessageObject()));
            return;
          }
        }
        // the userIdentity provided does not follow Authorization Identity
        // form. RFC3062 declaration "may or may not be an LDAPDN" allows
        // for pretty much anything in that field. we gonna try to parse it
        // as DN first then if that fails as user ID.
        // the userIdentity provided does not follow Authorization Identity form. RFC3062 declaration "may or may
        // not be an LDAPDN" allows for pretty much anything in that field. we gonna try to parse it as DN first
        // then if that fails as user ID.
        else
        {
          try
@@ -389,8 +354,7 @@
          }
          if (userDN != null && !userDN.isRootDN()) {
            // If the provided DN is an alternate DN for a root user,
            // then replace it with the actual root DN.
            // If the provided DN is an alternate DN for a root user, then replace it with the actual root DN.
            DN actualRootDN = DirectoryServer.getActualRootBindDN(userDN);
            if (actualRootDN != null) {
              userDN = actualRootDN;
@@ -410,8 +374,7 @@
          if (userEntry == null) {
            // The userIdentity was invalid.
            operation.setResultCode(ResultCode.PROTOCOL_ERROR);
            operation.appendErrorMessage(
              ERR_EXTOP_PASSMOD_INVALID_AUTHZID_STRING.get(authzIDStr));
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_INVALID_AUTHZID_STRING.get(authzIDStr));
            return;
          }
@@ -419,38 +382,30 @@
        }
      }
      // At this point, we should have the user entry.  Get the associated
      // password policy.
      // At this point, we should have the user entry.  Get the associated password policy.
      PasswordPolicyState pwPolicyState;
      try
      {
        AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry,
            false);
        AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry, false);
        if (!policy.isPasswordPolicy())
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_ACCOUNT_NOT_LOCAL.get(userDN));
          return;
        }
        pwPolicyState = (PasswordPolicyState) policy
          .createAuthenticationPolicyState(userEntry);
        pwPolicyState = (PasswordPolicyState) policy.createAuthenticationPolicyState(userEntry);
      }
      catch (DirectoryException de)
      {
        logger.traceException(de);
        operation.setResultCode(DirectoryServer.getServerErrorResultCode());
        operation.appendErrorMessage(
                ERR_EXTOP_PASSMOD_CANNOT_GET_PW_POLICY.get(userDN, de.getMessageObject()));
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_GET_PW_POLICY.get(userDN, de.getMessageObject()));
        return;
      }
      // Determine whether the user is changing his own password or if it's an
      // administrative reset.  If it's an administrative reset, then the
      // requester must have the PASSWORD_RESET privilege.
      // Determine whether the user is changing his own password or if it's an administrative reset.
      // If it's an administrative reset, then the requester must have the PASSWORD_RESET privilege.
      boolean selfChange;
      if (userIdentity == null)
      {
@@ -468,17 +423,14 @@
      if (! selfChange)
      {
        ClientConnection clientConnection = operation.getClientConnection();
        if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET,
                                            operation))
        if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, operation))
        {
          operation.appendErrorMessage(
                  ERR_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES.get());
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES.get());
          operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
          return;
        }
      }
      // See if the account is locked.  If so, then reject the request.
      if (pwPolicyState.isDisabled())
      {
@@ -486,27 +438,23 @@
        {
          pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
               new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
        }
        operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_ACCOUNT_DISABLED.get());
        return;
      }
      else if (selfChange &&
               (pwPolicyState.lockedDueToFailures() ||
                pwPolicyState.lockedDueToIdleInterval() ||
                pwPolicyState.lockedDueToMaximumResetAge()))
      else if (selfChange
          && (pwPolicyState.lockedDueToFailures()
              || pwPolicyState.lockedDueToIdleInterval()
              || pwPolicyState.lockedDueToMaximumResetAge()))
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
               new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
        }
        operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -514,28 +462,21 @@
        return;
      }
      // If the current password was provided, then we'll need to verify whether
      // it was correct.  If it wasn't provided but this is a self change, then
      // make sure that's OK.
      // If the current password was provided, then we'll need to verify whether it was correct.
      // If it wasn't provided but this is a self change, then make sure that's OK.
      if (oldPassword == null)
      {
        if (selfChange
            && pwPolicyState.getAuthenticationPolicy()
                .isPasswordChangeRequiresCurrentPassword())
            && pwPolicyState.getAuthenticationPolicy().isPasswordChangeRequiresCurrentPassword())
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
          operation.appendErrorMessage(
                  ERR_EXTOP_PASSMOD_REQUIRE_CURRENT_PW.get());
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_REQUIRE_CURRENT_PW.get());
          if (pwPolicyRequested)
          {
            pwPolicyErrorType =
                 PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
            pwPolicyErrorType = PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
            operation.addResponseControl(
                 new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                   pwPolicyWarningValue,
                                                   pwPolicyErrorType));
                 new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
          }
          return;
@@ -543,13 +484,11 @@
      }
      else
      {
        if (pwPolicyState.getAuthenticationPolicy()
            .isRequireSecureAuthentication()
        if (pwPolicyState.getAuthenticationPolicy().isRequireSecureAuthentication()
            && !operation.getClientConnection().isSecure())
        {
          operation.setResultCode(ResultCode.CONFIDENTIALITY_REQUIRED);
          operation.addAdditionalLogItem(AdditionalLogItem.quotedKeyValue(
              getClass(), "additionalInfo",
          operation.addAdditionalLogItem(AdditionalLogItem.quotedKeyValue(getClass(), "additionalInfo",
              ERR_EXTOP_PASSMOD_SECURE_AUTH_REQUIRED.get()));
          return;
        }
@@ -561,16 +500,14 @@
        else
        {
          operation.setResultCode(ResultCode.INVALID_CREDENTIALS);
          operation.addAdditionalLogItem(AdditionalLogItem.quotedKeyValue(
              getClass(), "additionalInfo",
          operation.addAdditionalLogItem(AdditionalLogItem.quotedKeyValue(getClass(), "additionalInfo",
              ERR_EXTOP_PASSMOD_INVALID_OLD_PASSWORD.get()));
          pwPolicyState.updateAuthFailureTimes();
          List<Modification> mods = pwPolicyState.getModifications();
          if (! mods.isEmpty())
          {
            InternalClientConnection conn =
                 InternalClientConnection.getRootConnection();
            InternalClientConnection conn = InternalClientConnection.getRootConnection();
            conn.processModify(userDN, mods);
          }
@@ -578,53 +515,39 @@
        }
      }
      // If it is a self password change and we don't allow that, then reject
      // the request.
      // If it is a self password change and we don't allow that, then reject the request.
      if (selfChange
          && !pwPolicyState.getAuthenticationPolicy()
              .isAllowUserPasswordChanges())
          && !pwPolicyState.getAuthenticationPolicy().isAllowUserPasswordChanges())
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
               new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
        }
        operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
        operation.appendErrorMessage(
                ERR_EXTOP_PASSMOD_USER_PW_CHANGES_NOT_ALLOWED.get());
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_USER_PW_CHANGES_NOT_ALLOWED.get());
        return;
      }
      // If we require secure password changes and the connection isn't secure,
      // then reject the request.
      if (pwPolicyState.getAuthenticationPolicy()
          .isRequireSecurePasswordChanges()
      // If we require secure password changes and the connection isn't secure, then reject the request.
      if (pwPolicyState.getAuthenticationPolicy().isRequireSecurePasswordChanges()
          && !operation.getClientConnection().isSecure())
      {
        operation.setResultCode(ResultCode.CONFIDENTIALITY_REQUIRED);
        operation.appendErrorMessage(
                ERR_EXTOP_PASSMOD_SECURE_CHANGES_REQUIRED.get());
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_SECURE_CHANGES_REQUIRED.get());
        return;
      }
      // If it's a self-change request and the user is within the minimum age,
      // then reject it.
      // If it's a self-change request and the user is within the minimum age, then reject it.
      if (selfChange && pwPolicyState.isWithinMinimumAge())
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_TOO_YOUNG;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
               new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
        }
        operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -632,33 +555,25 @@
        return;
      }
      // If the user's password is expired and it's a self-change request, then
      // see if that's OK.
      // If the user's password is expired and it's a self-change request, then see if that's OK.
      if (selfChange
          && pwPolicyState.isPasswordExpired()
          && !pwPolicyState.getAuthenticationPolicy()
              .isAllowExpiredPasswordChanges())
          && !pwPolicyState.getAuthenticationPolicy().isAllowExpiredPasswordChanges())
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
               new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
        }
        operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
        operation.appendErrorMessage(
                ERR_EXTOP_PASSMOD_PASSWORD_IS_EXPIRED.get());
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_PASSWORD_IS_EXPIRED.get());
        return;
      }
      // If the a new password was provided, then perform any appropriate
      // validation on it.  If not, then see if we can generate one.
      // If the a new password was provided, then perform any appropriate validation on it.
      // If not, then see if we can generate one.
      boolean generatedPassword = false;
      boolean isPreEncoded      = false;
      if (newPassword == null)
@@ -669,8 +584,7 @@
          if (newPassword == null)
          {
            operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
            operation.appendErrorMessage(
                    ERR_EXTOP_PASSMOD_NO_PW_GENERATOR.get());
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_NO_PW_GENERATOR.get());
            return;
          }
@@ -679,13 +593,24 @@
        catch (DirectoryException de)
        {
          logger.traceException(de);
          operation.setResultCode(de.getResultCode());
          operation.appendErrorMessage(
                  ERR_EXTOP_PASSMOD_CANNOT_GENERATE_PW.get(
                          de.getMessageObject()));
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_GENERATE_PW.get(de.getMessageObject()));
          return;
        }
        // Prepare to update the password history, if necessary.
        if (pwPolicyState.maintainHistory())
        {
          if (pwPolicyState.isPasswordInHistory(newPassword))
          {
            operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_PW_IN_HISTORY.get());
            return;
          }
          else
          {
            pwPolicyState.updatePasswordHistory();
          }
        }
      }
      else
      {
@@ -695,27 +620,22 @@
          // by an internal operation or during synchronization, so we don't
          // need to check for those cases.
          isPreEncoded = true;
          if (!pwPolicyState.getAuthenticationPolicy()
              .isAllowPreEncodedPasswords())
          if (!pwPolicyState.getAuthenticationPolicy().isAllowPreEncodedPasswords())
          {
            operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
            operation.appendErrorMessage(
                    ERR_EXTOP_PASSMOD_PRE_ENCODED_NOT_ALLOWED.get());
            operation.appendErrorMessage(ERR_EXTOP_PASSMOD_PRE_ENCODED_NOT_ALLOWED.get());
            return;
          }
        }
        else
        {
          // Run the new password through the set of password validators.
          if (selfChange
              || !pwPolicyState.getAuthenticationPolicy()
                  .isSkipValidationForAdministrators())
          if (selfChange || !pwPolicyState.getAuthenticationPolicy().isSkipValidationForAdministrators())
          {
            Set<ByteString> clearPasswords;
            if (oldPassword == null)
            {
              clearPasswords =
                   new HashSet<ByteString>(pwPolicyState.getClearPasswords());
              clearPasswords = new HashSet<ByteString>(pwPolicyState.getClearPasswords());
            }
            else
            {
@@ -731,19 +651,13 @@
            }
            LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
            if (! pwPolicyState.passwordIsAcceptable(operation, userEntry,
                                                     newPassword,
                                                     clearPasswords,
                                                     invalidReason))
            if (! pwPolicyState.passwordIsAcceptable(operation, userEntry, newPassword, clearPasswords, invalidReason))
            {
              if (pwPolicyRequested)
              {
                pwPolicyErrorType =
                     PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
                pwPolicyErrorType = PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
                operation.addResponseControl(
                     new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                       pwPolicyWarningValue,
                                                       pwPolicyErrorType));
                     new PasswordPolicyResponseControl(pwPolicyWarningType,  pwPolicyWarningValue, pwPolicyErrorType));
              }
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
@@ -753,19 +667,15 @@
            }
          }
          // Prepare to update the password history, if necessary.
          if (pwPolicyState.maintainHistory())
          {
            if (pwPolicyState.isPasswordInHistory(newPassword))
            {
              if (selfChange
                  || !pwPolicyState.getAuthenticationPolicy()
                      .isSkipValidationForAdministrators())
              if (selfChange || !pwPolicyState.getAuthenticationPolicy().isSkipValidationForAdministrators())
              {
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                operation.appendErrorMessage(
                        ERR_EXTOP_PASSMOD_PW_IN_HISTORY.get());
                operation.appendErrorMessage(ERR_EXTOP_PASSMOD_PW_IN_HISTORY.get());
                return;
              }
            }
@@ -777,7 +687,6 @@
        }
      }
      // Get the encoded forms of the new password.
      List<ByteString> encodedPasswords;
      if (isPreEncoded)
@@ -796,48 +705,37 @@
          logger.traceException(de);
          operation.setResultCode(de.getResultCode());
          operation.appendErrorMessage(
                  ERR_EXTOP_PASSMOD_CANNOT_ENCODE_PASSWORD.get(
                          de.getMessageObject()));
          operation.appendErrorMessage(ERR_EXTOP_PASSMOD_CANNOT_ENCODE_PASSWORD.get(de.getMessageObject()));
          return;
        }
      }
      // If the current password was provided, then remove all matching values
      // from the user's entry and replace them with the new password.
      // Otherwise replace all password values.
      AttributeType attrType = pwPolicyState.getAuthenticationPolicy()
          .getPasswordAttribute();
      // If the current password was provided, then remove all matching values from the user's entry
      // and replace them with the new password.  Otherwise replace all password values.
      AttributeType attrType = pwPolicyState.getAuthenticationPolicy().getPasswordAttribute();
      List<Modification> modList = new ArrayList<Modification>();
      if (oldPassword != null)
      {
        // Remove all existing encoded values that match the old password.
        Set<ByteString> existingValues = pwPolicyState.getPasswordValues();
        Set<ByteString> deleteValues =
             new LinkedHashSet<ByteString>(existingValues.size());
        Set<ByteString> deleteValues = new LinkedHashSet<ByteString>(existingValues.size());
        if (pwPolicyState.getAuthenticationPolicy().isAuthPasswordSyntax())
        {
          for (ByteString v : existingValues)
          {
            try
            {
              StringBuilder[] components =
                 AuthPasswordSyntax.decodeAuthPassword(v.toString());
              StringBuilder[] components = AuthPasswordSyntax.decodeAuthPassword(v.toString());
              PasswordStorageScheme<?> scheme =
                   DirectoryServer.getAuthPasswordStorageScheme(
                        components[0].toString());
                   DirectoryServer.getAuthPasswordStorageScheme(components[0].toString());
              if (scheme == null)
              {
                // The password is encoded using an unknown scheme.  Remove it
                // from the user's entry.
                // The password is encoded using an unknown scheme.  Remove it from the user's entry.
                deleteValues.add(v);
              }
              else
              {
                if (scheme.authPasswordMatches(oldPassword,
                                               components[1].toString(),
                                               components[2].toString()))
                if (scheme.authPasswordMatches(oldPassword, components[1].toString(), components[2].toString()))
                {
                  deleteValues.add(v);
                }
@@ -847,8 +745,7 @@
            {
              logger.traceException(de);
              // We couldn't decode the provided password value, so remove it
              // from the user's entry.
              // We couldn't decode the provided password value, so remove it from the user's entry.
              deleteValues.add(v);
            }
          }
@@ -859,21 +756,17 @@
          {
            try
            {
              String[] components =
                 UserPasswordSyntax.decodeUserPassword(v.toString());
              String[] components = UserPasswordSyntax.decodeUserPassword(v.toString());
              PasswordStorageScheme<?> scheme =
                   DirectoryServer.getPasswordStorageScheme(
                        toLowerCase(components[0]));
                   DirectoryServer.getPasswordStorageScheme(toLowerCase(components[0]));
              if (scheme == null)
              {
                // The password is encoded using an unknown scheme.  Remove it
                // from the user's entry.
                // The password is encoded using an unknown scheme.  Remove it from the user's entry.
                deleteValues.add(v);
              }
              else
              {
                if (scheme.passwordMatches(oldPassword,
                    ByteString.valueOf(components[1])))
                if (scheme.passwordMatches(oldPassword, ByteString.valueOf(components[1])))
                {
                  deleteValues.add(v);
                }
@@ -883,8 +776,7 @@
            {
              logger.traceException(de);
              // We couldn't decode the provided password value, so remove it
              // from the user's entry.
              // We couldn't decode the provided password value, so remove it from the user's entry.
              deleteValues.add(v);
            }
          }
@@ -895,7 +787,6 @@
        Attribute deleteAttr = builder.toAttribute();
        modList.add(new Modification(ModificationType.DELETE, deleteAttr));
        builder = new AttributeBuilder(attrType);
        builder.addAll(toAttributeValues(encodedPasswords));
        Attribute addAttr = builder.toAttribute();
@@ -909,32 +800,25 @@
        modList.add(new Modification(ModificationType.REPLACE, addAttr));
      }
      // Update the password changed time for the user entry.
      pwPolicyState.setPasswordChangedTime();
      // If the password was changed by an end user, then clear any reset flag
      // that might exist.  If the password was changed by an administrator,
      // then see if we need to set the reset flag.
      // If the password was changed by an end user, then clear any reset flag that might exist.
      // If the password was changed by an administrator, then see if we need to set the reset flag.
      if (selfChange)
      {
        pwPolicyState.setMustChangePassword(false);
      }
      else
      {
        pwPolicyState.setMustChangePassword(
             pwPolicyState.getAuthenticationPolicy().isForceChangeOnReset());
        pwPolicyState.setMustChangePassword(pwPolicyState.getAuthenticationPolicy().isForceChangeOnReset());
      }
      // Clear any record of grace logins, auth failures, and expiration
      // warnings.
      // Clear any record of grace logins, auth failures, and expiration warnings.
      pwPolicyState.clearFailureLockout();
      pwPolicyState.clearGraceLoginTimes();
      pwPolicyState.clearWarnedTime();
      // If the LDAP no-op control was included in the request, then set the
      // appropriate response.  Otherwise, process the operation.
      if (noOpRequested)
@@ -951,13 +835,10 @@
      // Get an internal connection and use it to perform the modification.
      boolean isRoot = DirectoryServer.isRootDN(requestorEntry.getName());
      AuthenticationInfo authInfo = new AuthenticationInfo(requestorEntry,
                                                           isRoot);
      InternalClientConnection internalConnection = new
           InternalClientConnection(authInfo);
      AuthenticationInfo authInfo = new AuthenticationInfo(requestorEntry, isRoot);
      InternalClientConnection internalConnection = new InternalClientConnection(authInfo);
      ModifyOperation modifyOperation =
           internalConnection.processModify(userDN, modList);
      ModifyOperation modifyOperation = internalConnection.processModify(userDN, modList);
      ResultCode resultCode = modifyOperation.getResultCode();
      if (resultCode != ResultCode.SUCCESS)
      {
@@ -967,7 +848,6 @@
        return;
      }
      // If there were any password policy state changes, we need to apply
      // them using a root connection because the end user may not have
      // sufficient access to apply them.  This is less efficient than
@@ -975,30 +855,24 @@
      List<Modification> pwPolicyMods = pwPolicyState.getModifications();
      if (! pwPolicyMods.isEmpty())
      {
        InternalClientConnection rootConnection =
             InternalClientConnection.getRootConnection();
        ModifyOperation modOp =
             rootConnection.processModify(userDN, pwPolicyMods);
        InternalClientConnection rootConnection = InternalClientConnection.getRootConnection();
        ModifyOperation modOp = rootConnection.processModify(userDN, pwPolicyMods);
        if (modOp.getResultCode() != ResultCode.SUCCESS)
        {
          // At this point, the user's password is already changed so there's
          // not much point in returning a non-success result.  However, we
          // should at least log that something went wrong.
          logger.warn(WARN_EXTOP_PASSMOD_CANNOT_UPDATE_PWP_STATE, userDN,
                  modOp.getResultCode(),
                  modOp.getErrorMessage());
          logger.warn(WARN_EXTOP_PASSMOD_CANNOT_UPDATE_PWP_STATE, userDN, modOp.getResultCode(),
              modOp.getErrorMessage());
        }
      }
      // If we've gotten here, then everything is OK, so indicate that the
      // operation was successful.
      // If we've gotten here, then everything is OK, so indicate that the operation was successful.
      operation.setResultCode(ResultCode.SUCCESS);
      // Save attachments for post-op plugins (e.g. Samba password plugin).
      operation.setAttachment(AUTHZ_DN_ATTACHMENT, userDN);
      operation.setAttachment(PWD_ATTRIBUTE_ATTACHMENT, pwPolicyState
          .getAuthenticationPolicy().getPasswordAttribute());
      operation.setAttachment(PWD_ATTRIBUTE_ATTACHMENT, pwPolicyState.getAuthenticationPolicy().getPasswordAttribute());
      if (!isPreEncoded)
      {
        operation.setAttachment(CLEAR_PWD_ATTACHMENT, newPassword);
@@ -1014,8 +888,7 @@
        try
        {
          writer.writeStartSequence();
          writer.writeOctetString(TYPE_PASSWORD_MODIFY_GENERATED_PASSWORD,
              newPassword);
          writer.writeOctetString(TYPE_PASSWORD_MODIFY_GENERATED_PASSWORD, newPassword);
          writer.writeEndSequence();
        }
        catch (IOException e)
@@ -1027,26 +900,20 @@
      }
      // If this was a self password change, and the client is authenticated
      // as the user whose password was changed, then clear the "must change
      // password" flag in the client connection.  Note that we're using the
      // authentication DN rather than the authorization DN in this case to
      // avoid mistakenly clearing the flag for the wrong user.
      if (selfChange && (authInfo.getAuthenticationDN() != null) &&
          (authInfo.getAuthenticationDN().equals(userDN)))
      // If this was a self password change, and the client is authenticated as the user whose password was changed,
      // then clear the "must change password" flag in the client connection.  Note that we're using the
      // authentication DN rather than the authorization DN in this case to avoid mistakenly clearing the flag
      // for the wrong user.
      if (selfChange && (authInfo.getAuthenticationDN() != null) && (authInfo.getAuthenticationDN().equals(userDN)))
      {
        operation.getClientConnection().setMustChangePassword(false);
      }
      // If the password policy control was requested, then add the
      // appropriate response control.
      // If the password policy control was requested, then add the appropriate response control.
      if (pwPolicyRequested)
      {
        operation.addResponseControl(
             new PasswordPolicyResponseControl(pwPolicyWarningType,
                                               pwPolicyWarningValue,
                                               pwPolicyErrorType));
             new PasswordPolicyResponseControl(pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType));
      }
      // Handle Account Status Notifications that may be needed.
@@ -1067,19 +934,15 @@
      {
        LocalizableMessage message = INFO_MODIFY_PASSWORD_CHANGED.get();
        pwPolicyState.generateAccountStatusNotification(
          AccountStatusNotificationType.PASSWORD_CHANGED,
          userEntry, message,
          AccountStatusNotification.createProperties(pwPolicyState, false,
                -1, currentPasswords, newPasswords));
            AccountStatusNotificationType.PASSWORD_CHANGED, userEntry, message,
            AccountStatusNotification.createProperties(pwPolicyState, false, -1, currentPasswords, newPasswords));
      }
      else
      {
        LocalizableMessage message = INFO_MODIFY_PASSWORD_RESET.get();
        pwPolicyState.generateAccountStatusNotification(
          AccountStatusNotificationType.PASSWORD_RESET,
          userEntry, message,
          AccountStatusNotification.createProperties(pwPolicyState, false,
                -1, currentPasswords, newPasswords));
            AccountStatusNotificationType.PASSWORD_RESET, userEntry, message,
            AccountStatusNotification.createProperties(pwPolicyState, false, -1, currentPasswords, newPasswords));
      }
    }
    finally
@@ -1097,22 +960,19 @@
  }
  /**
   * Retrieves the entry for the specified user based on the provided DN.  If
   * any problem is encountered or the requested entry does not exist, then the
   * provided operation will be updated with appropriate result information and
   * this method will return <CODE>null</CODE>.  The caller must hold a write
   * lock on the specified entry.
   * Retrieves the entry for the specified user based on the provided DN.  If any problem is encountered or
   * the requested entry does not exist, then the provided operation will be updated with appropriate result
   * information and this method will return <CODE>null</CODE>.
   * The caller must hold a write lock on the specified entry.
   *
   * @param  operation  The extended operation being processed.
   * @param  entryDN    The DN of the user entry to retrieve.
   *
   * @return  The requested entry, or <CODE>null</CODE> if there was no such
   *          entry or it could not be retrieved.
   * @return  The requested entry, or <CODE>null</CODE> if there was no such entry or it could not be retrieved.
   */
  private Entry getEntryByDN(ExtendedOperation operation, DN entryDN)
  {
    // Retrieve the user's entry from the directory.  If it does not exist, then
    // fail.
    // Retrieve the user's entry from the directory.  If it does not exist, then fail.
    try
    {
      Entry userEntry = DirectoryServer.getEntry(entryDN);
@@ -1120,8 +980,7 @@
      if (userEntry == null)
      {
        operation.setResultCode(ResultCode.NO_SUCH_OBJECT);
        operation.appendErrorMessage(
                ERR_EXTOP_PASSMOD_NO_USER_ENTRY_BY_AUTHZID.get(entryDN));
        operation.appendErrorMessage(ERR_EXTOP_PASSMOD_NO_USER_ENTRY_BY_AUTHZID.get(entryDN));
        // See if one of the entry's ancestors exists.
        operation.setMatchedDN(findMatchedDN(entryDN));
@@ -1168,35 +1027,29 @@
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAcceptable(ExtendedOperationHandlerCfg
                                                configuration,
  public boolean isConfigurationAcceptable(ExtendedOperationHandlerCfg configuration,
                                           List<LocalizableMessage> unacceptableReasons)
  {
    PasswordModifyExtendedOperationHandlerCfg config =
         (PasswordModifyExtendedOperationHandlerCfg) configuration;
    PasswordModifyExtendedOperationHandlerCfg config = (PasswordModifyExtendedOperationHandlerCfg) configuration;
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   * Indicates whether the provided configuration entry has an acceptable configuration for this component.
   * If it does not, then detailed information about the problem(s) should be added to the provided list.
   *
   * @param  config          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   * @param  config          The configuration entry for which to make the determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about why the provided entry does not
   *                              have an acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   * @return  <CODE>true</CODE> if the provided entry has an acceptable configuration for this component,
   *          or <CODE>false</CODE> if not.
   */
  @Override
  public boolean isConfigurationChangeAcceptable(
       PasswordModifyExtendedOperationHandlerCfg config,
       List<LocalizableMessage> unacceptableReasons)
  public boolean isConfigurationChangeAcceptable(PasswordModifyExtendedOperationHandlerCfg config,
                                                 List<LocalizableMessage> unacceptableReasons)
  {
    // Make sure that the specified identity mapper is OK.
    try
@@ -1217,7 +1070,6 @@
      return false;
    }
    // If we've gotten here, then everything is OK.
    return true;
  }
@@ -1225,26 +1077,21 @@
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   * Makes a best-effort attempt to apply the configuration contained in the provided entry.
   * Information about the result of this processing should be added to the provided message list.
   * Information should always be added to this list if a configuration change could not be applied.
   * If detailed results are requested, then information about the changes applied successfully (and optionally
   * about parameters that were not changed) should also be included.
   *
   * @param  config      The entry containing the new configuration to
   *                          apply for this component.
   * @param  config      The entry containing the new configuration to apply for this component.
   *
   * @return  Information about the result of the configuration update.
   */
  @Override
  public ConfigChangeResult applyConfigurationChange(
       PasswordModifyExtendedOperationHandlerCfg config)
  public ConfigChangeResult applyConfigurationChange(PasswordModifyExtendedOperationHandlerCfg config)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    // Make sure that the specified identity mapper is OK.
    DN             mapperDN = null;
    IdentityMapper<?> mapper   = null;
@@ -1266,7 +1113,6 @@
      ccr.addMessage(ERR_EXTOP_PASSMOD_CANNOT_DETERMINE_ID_MAPPER.get(config.dn(), getExceptionMessage(e)));
    }
    // If all of the changes were acceptable, then apply them.
    if (ccr.getResultCode() == ResultCode.SUCCESS
        && ! identityMapperDN.equals(mapperDN))
@@ -1275,7 +1121,6 @@
      identityMapperDN = mapperDN;
    }
    // Save this configuration for future reference.
    currentConfig = config;
opendj-server-legacy/src/test/java/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java
@@ -89,8 +89,7 @@
         "objectClass: ds-cfg-extended-operation-handler",
         "objectClass: ds-cfg-password-modify-extended-operation-handler",
         "cn: Password Modify",
         "ds-cfg-java-class: org.opends.server." +
              "extensions.PasswordModifyExtendedOperation",
         "ds-cfg-java-class: org.opends.server.extensions.PasswordModifyExtendedOperation",
         "ds-cfg-enabled: true",
         "",
         "dn: cn=Password Modify,cn=Extended Operations,cn=config",
@@ -98,8 +97,7 @@
         "objectClass: ds-cfg-extended-operation-handler",
         "objectClass: ds-cfg-password-modify-extended-operation-handler",
         "cn: Password Modify",
         "ds-cfg-java-class: org.opends.server." +
              "extensions.PasswordModifyExtendedOperation",
         "ds-cfg-java-class: org.opends.server.extensions.PasswordModifyExtendedOperation",
         "ds-cfg-enabled: true",
         "ds-cfg-identity-mapper: invaliddn",
         "",
@@ -108,8 +106,7 @@
         "objectClass: ds-cfg-extended-operation-handler",
         "objectClass: ds-cfg-password-modify-extended-operation-handler",
         "cn: Password Modify",
         "ds-cfg-java-class: org.opends.server." +
              "extensions.PasswordModifyExtendedOperation",
         "ds-cfg-java-class: org.opends.server.extensions.PasswordModifyExtendedOperation",
         "ds-cfg-enabled: true",
         "ds-cfg-identity-mapper: cn=nonexistent,cn=config");
@@ -131,27 +128,21 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "invalidConfigs",
        expectedExceptions = { ConfigException.class,
                               InitializationException.class })
  @Test(dataProvider = "invalidConfigs", expectedExceptions = { ConfigException.class, InitializationException.class})
  public void testInitializeWithInvalidConfigs(Entry e)
         throws Exception
  {
    PasswordModifyExtendedOperationHandlerCfg configuration =
         AdminTestCaseUtils.getConfiguration(
              PasswordModifyExtendedOperationHandlerCfgDefn.getInstance(),
              e);
         AdminTestCaseUtils.getConfiguration(PasswordModifyExtendedOperationHandlerCfgDefn.getInstance(), e);
    PasswordModifyExtendedOperation handler =
         new PasswordModifyExtendedOperation();
    PasswordModifyExtendedOperation handler = new PasswordModifyExtendedOperation();
    handler.initializeExtendedOperationHandler(configuration);
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a root user</LI>
@@ -176,17 +167,9 @@
      "-c", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(DN.valueOf("cn=Directory Manager"),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(DN.valueOf("cn=Directory Manager"), "newPassword");
    // Now change the password back to what it was.
    args = new String[]
@@ -199,15 +182,12 @@
      "-c", "newPassword",
      "-n", "password"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a root user</LI>
@@ -233,17 +213,9 @@
      "-n", "newPassword",
      "-A"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(DN.valueOf("cn=Directory Manager"),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(DN.valueOf("cn=Directory Manager"), "newPassword");
    // Now change the password back to what it was.
    args = new String[]
@@ -257,18 +229,15 @@
      "-n", "password",
      "-A"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Unauthenticated client conection</LI>
   *   <LI>Unauthenticated client connection</LI>
   *   <LI>Authorization ID provided</LI>
   *   <LI>Current password provided</LI>
   *   <LI>New password provided</LI>
@@ -289,17 +258,9 @@
      "-c", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(DN.valueOf("cn=Directory Manager"),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(DN.valueOf("cn=Directory Manager"), "newPassword");
    // Now change the password back to what it was.
    args = new String[]
@@ -311,15 +272,12 @@
      "-c", "newPassword",
      "-n", "password"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -335,18 +293,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -359,22 +306,14 @@
      "-c", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -390,18 +329,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -413,22 +341,15 @@
      "-w", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -466,15 +387,12 @@
      "-D", "uid=test.user,o=test",
      "-w", "password"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -490,18 +408,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -514,22 +421,15 @@
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -545,18 +445,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -569,22 +458,15 @@
      "-a", "u:test.user",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -600,18 +482,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -624,22 +495,15 @@
      "-a", "uid=test.user,o=test",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a normal user</LI>
@@ -655,18 +519,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
@@ -679,22 +532,15 @@
      "-a", "test.user",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Unauthenticated client connection</LI>
@@ -710,18 +556,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
    {
@@ -732,22 +567,15 @@
      "-c", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Unauthenticated client connection</LI>
@@ -763,18 +591,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
    {
@@ -785,22 +602,15 @@
      "-c", "password",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP.  It will use the
   * following configuration:
   * Tests the password modify extended operation over LDAP.  It will use the following configuration:
   * <BR>
   * <UL>
   *   <LI>Authenticated as a root user</LI>
@@ -816,18 +626,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    String[] args =
    {
@@ -839,22 +638,16 @@
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword"
    };
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSimpleBind(userEntry.getName(),
                                ByteString.valueOf("newPassword"));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    verifyPasswordPerformingInternalBind(userEntry.getName(), "newPassword");
  }
  /**
   * Tests the password modify extended operation over LDAP using an
   * authorization ID with the DN of a user that doesn't exist.
   * Tests the password modify extended operation over LDAP using an authorization ID
   * with the DN of a user that doesn't exist.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -876,16 +669,13 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over LDAP using an
   * authorization ID with a malformed DN.
   * Tests the password modify extended operation over LDAP using an authorization ID with a malformed DN.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -907,17 +697,14 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over LDAP using an
   * authorization ID with the DN of a user where no part of the hierarchy
   * exists.
   * Tests the password modify extended operation over LDAP using an authorization ID with the DN
   * of a user where no part of the hierarchy exists.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -939,16 +726,14 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over LDAP using an
   * authorization ID with the uid of a user that doesn't exist.
   * Tests the password modify extended operation over LDAP using an authorization ID with the uid
   * of a user that doesn't exist.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -970,17 +755,14 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over LDAP using an
   * authorization ID with the uid of a user that doesn't exist and providing
   * the current password..
   * Tests the password modify extended operation over LDAP using an authorization ID with the uid
   * of a user that doesn't exist and providing the current password.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -1003,16 +785,13 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 32);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 32);
  }
  /**
   * Tests the password modify extended operation over LDAP using a malformed
   * authorization ID.
   * Tests the password modify extended operation over LDAP using a malformed authorization ID.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -1034,16 +813,13 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over LDAP using a bad
   * current password.
   * Tests the password modify extended operation over LDAP using a bad current password.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -1053,17 +829,7 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    String[] args =
    {
@@ -1077,17 +843,13 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 49);
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 49);
  }
  /**
   * Tests the password modify extended operation over LDAP using a pre-encoded
   * new password.
   *
   * Tests the password modify extended operation over LDAP using a pre-encoded new password.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1096,27 +858,12 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-pre-encoded-passwords";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    String[] args =
@@ -1130,23 +877,17 @@
      "-n", "{SSHA}Fv4b7f4AnRMUiGqBi9QA1xJrTtRTqS3WpRi81g=="
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation over LDAP using a pre-encoded
   * new password.
   * Tests the password modify extended operation over LDAP using a pre-encoded new password.
   *
   *
   * @throws  Exception  If an unexpected error occurs.
@@ -1156,26 +897,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    /* Make sure preEncoded passwords are rejected */
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-pre-encoded-passwords";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
        Attributes.create(attr, "false")));
    ModifyOperation modifyOperation = getRootConnection().processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "false");
    String[] args =
    {
@@ -1188,17 +916,14 @@
      "-n", "{SSHA}Fv4b7f4AnRMUiGqBi9QA1xJrTtRTqS3WpRi81g=="
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    // don't restore password policy as this is already the default.
  }
  /**
   * Tests the password modify extended operation over LDAP using a pre-encoded
   * new password.
   *
   * Tests the password modify extended operation over LDAP using a pre-encoded new password.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1207,26 +932,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    /* Make sure preEncoded passwords are rejected */
    /* Make sure preEncoded passwords are rejected. This should be the default, so we will not restore config after */
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-pre-encoded-passwords";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
        Attributes.create(attr, "false")));
    ModifyOperation modifyOperation = getRootConnection().processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "false");
    String[] args =
    {
@@ -1238,35 +950,30 @@
      "-n", "{SSHA}Fv4b7f4AnRMUiGqBi9QA1xJrTtRTqS3WpRi81g=="
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
  }
  /**
   * Tests the password modify extended operation over an internal connection
   * with a request whose value isn't a valid encoded sequence.
   * Tests the password modify extended operation over an internal connection with a request whose value
   * isn't a valid encoded sequence.
   */
  @Test
  public void testFailureInvalidRequestValueFormat()
  {
    ByteString requestValue = ByteString.valueOf("malformed");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    ExtendedOperation extOp =
         conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST,
                                       requestValue);
    InternalClientConnection conn = getRootConnection();
    ExtendedOperation extOp = conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST, requestValue);
    assertFalse(extOp.getResultCode() == ResultCode.SUCCESS);
  }
  /**
   * Tests the password modify extended operation over an internal connection
   * with a request that contain an invalid sequence element type.
   * Tests the password modify extended operation over an internal connection with a request that contain
   * an invalid sequence element type.
   */
  @Test
  public void testFailureInvalidSequenceElementType() throws Exception
@@ -1278,19 +985,16 @@
    writer.writeEndSequence();
    ByteString requestValue = builder.toByteString();
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    ExtendedOperation extOp =
         conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST,
                                       requestValue);
    InternalClientConnection conn = getRootConnection();
    ExtendedOperation extOp = conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST, requestValue);
    assertFalse(extOp.getResultCode() == ResultCode.SUCCESS);
  }
  /**
   * Tests the password modify extended operation over an unauthenticated
   * internal connection and without providing an authorization ID.
   * Tests the password modify extended operation over an unauthenticated internal connection
   * and without providing an authorization ID.
   */
  @Test
  public void testFailureCompletelyAnonymous() throws Exception
@@ -1298,24 +1002,20 @@
    ByteStringBuilder builder = new ByteStringBuilder();
    ASN1Writer writer = ASN1.getWriter(builder);
    writer.writeStartSequence();
    writer.writeOctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD,
                                     "newPassword");
    writer.writeOctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD, "newPassword");
    writer.writeEndSequence();
    ByteString requestValue = builder.toByteString();
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    ExtendedOperation extOp =
         conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST,
                                       requestValue);
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    ExtendedOperation extOp = conn.processExtendedOperation(OID_PASSWORD_MODIFY_REQUEST, requestValue);
    assertFalse(extOp.getResultCode() == ResultCode.SUCCESS);
  }
  /**
   * Tests the password modify extended operation with a password policy that
   * doesn't allow users to change their own passwords.
   * Tests the password modify extended operation with a password policy that doesn't allow users
   * to change their own passwords. The current password is not provided in the extended operation.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1324,29 +1024,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-user-password-changes";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "false");
    String[] args =
    {
@@ -1358,23 +1042,18 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "true");
    }
  }
  /**
   * Tests the password modify extended operation with a password policy that
   * doesn't allow users to change their own passwords.
   * Tests the password modify extended operation with a password policy that doesn't allow users to change
   * their own passwords. The current password is provided.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1383,29 +1062,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-user-password-changes";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "false");
    String[] args =
    {
@@ -1418,23 +1081,18 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 53);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 53);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "true");
    }
  }
  /**
   * Tests the password modify extended operation without providing the current
   * password but with a password policy that requires it.
   * Tests the password modify extended operation without providing the current password
   * but with a password policy that requires it.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1443,27 +1101,12 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-password-change-requires-current-password";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    String[] args =
@@ -1477,23 +1120,19 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    } finally {
      // Reset to default configuration
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation that requires secure
   * authentication but a connection that doesn't provide it.
   * Tests the password modify extended operation that requires secure authentication
   * but a connection that doesn't provide it.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1502,28 +1141,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-require-secure-authentication";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    String[] args =
@@ -1536,23 +1160,19 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 13);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 13);
    } finally {
      // Reset to default configuration
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation that requires secure
   * password changes but a connection that doesn't provide it.
   * Tests the password modify extended operation that requires secure password changes
   * but a connection that doesn't provide it, using self-authentication.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1561,28 +1181,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-require-secure-password-changes";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    String[] args =
@@ -1595,23 +1200,18 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 13);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation that requires secure
   * password changes but a connection that doesn't provide it.
   * Tests the password modify extended operation that requires secure password changes
   * but a connection that doesn't provide it. Authenticating as Directory Manager.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1620,28 +1220,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-require-secure-password-changes";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    String[] args =
@@ -1649,29 +1234,25 @@
      "--noPropertiesFile",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "uid=test.user,o=test",
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "uid=test.user,o=test",
      "-c", "password",
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 13);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 13);
    } finally {
      // Reset to default configuration
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation with a password change that is
   * within the minimum password age.
   * Tests the password modify extended operation with a password change that is within the minimum password age.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1680,29 +1261,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-min-password-age";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "24 hours")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "24 hours");
    String[] args =
    {
@@ -1713,23 +1278,18 @@
      "-w", "password",
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "0 seconds")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "0 seconds");
    }
  }
  /**
   * Tests the password modify extended operation with a password change that is
   * within the minimum password age.
   * Tests the password modify extended operation with a password change that is within the minimum password age.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1738,29 +1298,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-min-password-age";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "24 hours")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "24 hours");
    String[] args =
    {
@@ -1772,24 +1316,19 @@
      "-c", "password",
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 53);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "0 seconds")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 53);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "0 seconds");
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user whose password is expired but expired password changes are not
   * allowed.
   * Tests the password modify extended operation with a password change for a user whose password is expired
   * but expired password changes are not allowed.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1798,68 +1337,40 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr1 = "ds-cfg-max-password-age";
    applyPwdPolicyMods(conn, dnStr, attr1, "90 days");
    String attr2 = "ds-cfg-expire-passwords-without-warning";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr1, "90 days")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr2, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr2, "true");
    try {
      setPasswordChangedTime(conn, userEntry);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create("pwdchangedtime",
                                            "20050101000000.000Z")));
    modifyOperation = conn.processModify(userEntry.getName(), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
      String[] args =
      {
        "--noPropertiesFile",
        "-h", "127.0.0.1",
        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-a", "dn:uid=test.user,o=test",
        "-c", "password",
        "-n", "newPassword"
      };
    String[] args =
    {
      "--noPropertiesFile",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-a", "dn:uid=test.user,o=test",
      "-c", "password",
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr1, "0 seconds")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr2, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr1, "0 seconds");
      applyPwdPolicyMods(conn, dnStr, attr2, "false");
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user whose password is expired but expired password changes are allowed.
   * Tests the password modify extended operation with a password change for a user whose password is expired
   * but expired password changes are allowed.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -1868,136 +1379,81 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry userEntry = TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "ds-privilege-name: bypass-acl",
         "userPassword: password");
    Entry userEntry = addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr1 = "ds-cfg-max-password-age";
    applyPwdPolicyMods(conn, dnStr, attr1, "90 days");
    String attr2 = "ds-cfg-expire-passwords-without-warning";
    applyPwdPolicyMods(conn, dnStr, attr2, "true");
    String attr3 = "ds-cfg-allow-expired-password-changes";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr1, "90 days")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr2, "true")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr3, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr3, "true");
    try {
      setPasswordChangedTime(conn, userEntry);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create("pwdchangedtime",
                                            "20050101000000.000Z")));
    modifyOperation = conn.processModify(userEntry.getName(), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
      String[] args =
      {
        "--noPropertiesFile",
        "-h", "127.0.0.1",
        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-a", "dn:uid=test.user,o=test",
        "-c", "password",
        "-n", "newPassword"
      };
    String[] args =
    {
      "--noPropertiesFile",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-a", "dn:uid=test.user,o=test",
      "-c", "password",
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr1, "0 seconds")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr2, "false")));
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr3, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    }
    finally {
      applyPwdPolicyMods(conn, dnStr, attr1, "0 seconds");
      applyPwdPolicyMods(conn, dnStr, attr2, "false");
      applyPwdPolicyMods(conn, dnStr, attr3, "false");
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user where there is no new password provided and there is no password
   * generator.
   * Tests the password modify extended operation with a password change for a user
   * where there is no new password provided and there is no password generator.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
  @Test
  public void testFailureNoPasswordNoGenerator()
         throws Exception
  {
         throws Exception {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr = "ds-cfg-password-generator";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                      Attributes.empty(DirectoryServer.getAttributeType(attr))));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, null);
    String[] args =
    {
      "--noPropertiesFile",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "uid=test.user,o=test",
      "-w", "password"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    String genDN =
         "cn=Random Password Generator,cn=Password Generators,cn=config";
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, genDN)));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
        {
            "--noPropertiesFile",
            "-h", "127.0.0.1",
            "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
            "-D", "uid=test.user,o=test",
            "-w", "password"
        };
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "cn=Random Password Generator,cn=Password Generators,cn=config");
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user where there is no new password provided and there is no password
   * generator.
   * Tests the password modify extended operation with a password change for a user where there is no new password
   * provided and there is no password generator.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -2006,29 +1462,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr = "ds-cfg-password-generator";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                      Attributes.empty(DirectoryServer.getAttributeType(attr))));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, null);
    String[] args =
    {
@@ -2038,25 +1478,19 @@
      "-a", "dn:uid=test.user,o=test",
      "-c", "password"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    String genDN =
         "cn=Random Password Generator,cn=Password Generators,cn=config";
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, genDN)));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    }
    finally {
      applyPwdPolicyMods(conn, dnStr, attr, "cn=Random Password Generator,cn=Password Generators,cn=config");
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user where a password validator rejects the change.
   * Tests the password modify extended operation with a password change for a user where a password validator
   * rejects the change.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -2065,30 +1499,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr = "ds-cfg-password-validator";
    String valDN =
         "cn=Length-Based Password Validator,cn=Password Validators,cn=config";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, valDN)));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "cn=Length-Based Password Validator,cn=Password Validators,cn=config");
    String[] args =
@@ -2100,23 +1517,19 @@
      "-w", "password",
      "-n", "short"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                      Attributes.empty(DirectoryServer.getAttributeType(attr))));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    }
    finally {
      applyPwdPolicyMods(conn, dnStr, attr, null);
    }
  }
  /**
   * Tests the password modify extended operation with a password change for a
   * user where a password validator rejects the change.
   * Tests the password modify extended operation with a password change for a user where a password validator
   * rejects the change.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -2125,31 +1538,13 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User",
         "userPassword: password");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr = "ds-cfg-password-validator";
    String valDN =
         "cn=Length-Based Password Validator,cn=Password Validators,cn=config";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, valDN)));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "cn=Length-Based Password Validator,cn=Password Validators,cn=config");
    String[] args =
    {
@@ -2160,20 +1555,15 @@
      "-c", "password",
      "-n", "short"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertFalse(exitCode == 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                      Attributes.empty(DirectoryServer.getAttributeType(attr))));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, null));
    }
    finally {
      applyPwdPolicyMods(conn, dnStr, attr, null);
    }
  }
  /**
   * Tests the password modify extended operation over LDAP when the existing
   * account has multiple passwords.
@@ -2188,16 +1578,10 @@
    TestCaseUtils.initializeTestBackend(true);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-multiple-password-values";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    InternalClientConnection conn = getRootConnection();
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    TestCaseUtils.addEntry(
        "dn: uid=test.user,o=test",
@@ -2225,24 +1609,17 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests the password modify extended operation over LDAP when the existing
   * account has multiple passwords.
   *
   * Tests the password modify extended operation over LDAP when the existing account has multiple passwords.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -2252,16 +1629,10 @@
  {
    TestCaseUtils.initializeTestBackend(true);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=SHA1 AuthPassword Policy,cn=Password Policies,cn=config";
    String attr  = "ds-cfg-allow-multiple-password-values";
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "true")));
    ModifyOperation modifyOperation =
         conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    applyPwdPolicyMods(conn, dnStr, attr, "true");
    TestCaseUtils.addEntry(
        "dn: uid=test.user,o=test",
@@ -2276,8 +1647,7 @@
        "cn: Test User",
        "authPassword: password",
        "authPassword: password2",
        "ds-pwp-password-policy-dn: cn=SHA1 AuthPassword Policy," +
        "cn=Password Policies,cn=config");
        "ds-pwp-password-policy-dn: cn=SHA1 AuthPassword Policy,cn=Password Policies,cn=config");
    String[] args =
@@ -2292,23 +1662,18 @@
      "-n", "newPassword"
    };
    int exitCode =
         LDAPPasswordModify.mainPasswordModify(args, false, null, null);
    assertEquals(exitCode, 0);
    mods.clear();
    mods.add(new Modification(ModificationType.REPLACE,
                              Attributes.create(attr, "false")));
    modifyOperation = conn.processModify(DN.valueOf(dnStr), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
    try {
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, System.err), 0);
    } finally {
      applyPwdPolicyMods(conn, dnStr, attr, "false");
    }
  }
  /**
   * Tests to ensure that if the user provides the correct old password, then
   * the last login time will be updated if that feature is enabled.
   * Tests to ensure that if the user provides the correct old password, then the last login time will be
   * updated if that feature is enabled.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
@@ -2317,27 +1682,8 @@
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    addDefaultTestEntry();
    TestCaseUtils.applyModifications(false,
      "dn: uid=test.user,o=test",
      "changetype: add",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: oldpassword",
      // FIXME -- I shouldn't have to add this ACI explicitly, but for some
      //          reason the global ACIs are getting removed and never put back,
      //          and without this ACI the user won't have permission to change
      //          its own password.  If we ever change the access control syntax
      //          then this will likely need to be updated, but I don't want to
      //          have to give the user the bypass-acl privilege.
      "aci: (targetattr=\"*\")(version 3.0; acl \"Self Modify Rights\"; " +
           "allow (read,search,compare,write) userdn=\"ldap:///self\";)");
    TestCaseUtils.applyModifications(true,
      "dn: cn=Default Password Policy,cn=Password Policies,cn=config",
      "changetype: modify",
@@ -2364,13 +1710,11 @@
        "-h", "127.0.0.1",
        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-a", "dn:uid=test.user,o=test",
        "-c", "oldpassword",
        "-c", "password",
        "-n", "newpassword"
      };
      int exitCode = LDAPPasswordModify.mainPasswordModify(args, false, null,
                                                           System.err);
      assertEquals(exitCode, 0);
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, System.err), 0);
      userEntry = DirectoryServer.getEntry(userDN);
      assertNotNull(userEntry);
@@ -2388,7 +1732,6 @@
  }
  /**
   * Tests to ensure that if the user provides an incorrect old password, then
   * the auth failure times will be updated if that feature is enabled.
@@ -2401,23 +1744,12 @@
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.applyModifications(false,
      "dn: uid=test.user,o=test",
      "changetype: add",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: oldpassword");
    TestCaseUtils.applyModifications(true,
      "dn: cn=Default Password Policy,cn=Password Policies,cn=config",
      "changetype: modify",
      "replace: ds-cfg-lockout-failure-count",
      "ds-cfg-lockout-failure-count: 3");
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    applyPwdPolicyMods(conn, dnStr,
        "ds-cfg-lockout-failure-count", "3");
    try
    {
@@ -2440,22 +1772,110 @@
        "-n", "newpassword"
      };
      int exitCode = LDAPPasswordModify.mainPasswordModify(args, false, null,
                                                           System.err);
      assertFalse(exitCode == 0);
      assertFalse(0 == LDAPPasswordModify.mainPasswordModify(args, false, null, System.err));
      userEntry = DirectoryServer.getEntry(userDN);
      assertNotNull(userEntry);
      assertTrue(userEntry.hasAttribute(authFailureTimesAttr));
    } finally {
      applyPwdPolicyMods(conn, dnStr,
          "ds-cfg-lockout-failure-count", "0");
    }
  }
  /**
   * Tests to ensure that if the password is changed with a generated password, the pwdHistory is getting
   * updated properly.
   *
   * @throws  Exception  If an unexpected error occurs.
   */
  @Test()
  public void testUpdatePasswordHistory()
      throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    addDefaultTestEntry();
    InternalClientConnection conn = getRootConnection();
    String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
    applyPwdPolicyMods(conn, dnStr ,
        "ds-cfg-password-history-count", "5");
    try
    {
      AttributeType pwdHistoryAttr = DirectoryServer.getAttributeType("pwdhistory", false);
      assertNotNull(pwdHistoryAttr);
      DN userDN = DN.valueOf("uid=test.user,o=test");
      Entry userEntry = DirectoryServer.getEntry(userDN);
      assertNotNull(userEntry);
      assertFalse(userEntry.hasAttribute(pwdHistoryAttr));
      String[] args =
      {
        "--noPropertiesFile",
        "-h", "127.0.0.1",
        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-a", "dn:uid=test.user,o=test",
        "-c", "password"
      };
      assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, System.err), 0);
      userEntry = DirectoryServer.getEntry(userDN);
      assertNotNull(userEntry);
      assertTrue(userEntry.hasAttribute(pwdHistoryAttr));
    }
    finally
    {
      TestCaseUtils.applyModifications(true,
        "dn: cn=Default Password Policy,cn=Password Policies,cn=config",
        "changetype: modify",
        "replace: ds-cfg-lockout-failure-count",
        "ds-cfg-lockout-failure-count: 0");
      applyPwdPolicyMods(conn, dnStr,
          "ds-cfg-password-history-count", "0");
    }
  }
  private Entry addDefaultTestEntry() throws Exception {
    return TestCaseUtils.addEntry(
        "dn: uid=test.user,o=test",
        "objectClass: top",
        "objectClass: person",
        "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson",
        "uid: test.user",
        "givenName: Test",
        "sn: User",
        "cn: Test User",
        "ds-privilege-name: bypass-acl",
        "userPassword: password");
  }
  private void verifyPasswordPerformingInternalBind(DN name, String newPwd) throws DirectoryException {
    // Perform an internal bind to verify the password was actually changed.
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
        conn.processSimpleBind(name, ByteString.valueOf(newPwd));
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
  private void applyPwdPolicyMods(InternalClientConnection conn, String pwPolDN, String attr, String value)
      throws DirectoryException
  {
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
        value == null ? Attributes.empty(attr) : Attributes.create(attr, value)));
    ModifyOperation modifyOperation = conn.processModify(DN.valueOf(pwPolDN), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
  }
  private void setPasswordChangedTime(InternalClientConnection conn, Entry userEntry) {
    ArrayList<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
        Attributes.create("pwdchangedtime",
            "20050101000000.000Z")));
    ModifyOperation modifyOperation = conn.processModify(userEntry.getName(), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
  }
}