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

neil_a_wilson
30.50.2006 56100637ded7f7f68c9a60436e3fe0c792961a02
opendj-sdk/opends/src/server/org/opends/server/api/ExtendedOperationHandler.java
@@ -28,6 +28,9 @@
import java.util.HashSet;
import java.util.Set;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.ExtendedOperation;
@@ -53,6 +56,16 @@
  // The default set of supported control OIDs for this extended
  // operation.
  private Set<String> supportedControlOIDs = new HashSet<String>(0);
  // The default set of supported feature OIDs for this extended
  // operation.
  private Set<String> supportedFeatureOIDs = new HashSet<String>(0);
  /**
   * Initializes this extended operation handler based on the
   * information in the provided configuration entry.  It should also
@@ -98,5 +111,79 @@
   */
  public abstract void processExtendedOperation(ExtendedOperation
                                                     operation);
  /**
   * Retrieves the OIDs of the controls that may be supported by this
   * extended operation handler.  It should be overridden by any
   * extended operation handler which provides special support for one
   * or more controls.
   *
   * @return  The OIDs of the controls that may be supported by this
   *          extended operation handler.
   */
  public Set<String> getSupportedControls()
  {
    assert debugEnter(CLASS_NAME, "getSupportedControls");
    return supportedControlOIDs;
  }
  /**
   * Indicates whether this extended operation handler supports the
   * specified control.
   *
   * @param  controlOID  The OID of the control for which to make the
   *                     determination.
   *
   * @return  {@code true} if this extended operation handler does
   *          support the requested control, or {@code false} if not.
   */
  public final boolean supportsControl(String controlOID)
  {
    assert debugEnter(CLASS_NAME, "supportsControl",
                      String.valueOf(controlOID));
    return getSupportedControls().contains(controlOID);
  }
  /**
   * Retrieves the OIDs of the features that may be supported by this
   * extended operation handler.
   *
   * @return  The OIDs of the features that may be supported by this
   *          extended operation handler.
   */
  public Set<String> getSupportedFeatures()
  {
    assert debugEnter(CLASS_NAME, "getSupportedFeatures");
    return supportedFeatureOIDs;
  }
  /**
   * Indicates whether this extended operation handler supports the
   * specified feature.
   *
   * @param  featureOID  The OID of the feature for which to make the
   *                     determination.
   *
   * @return  {@code true} if this extended operation handler does
   *          support the requested feature, or {@code false} if not.
   */
  public final boolean supportsFeature(String featureOID)
  {
    assert debugEnter(CLASS_NAME, "supportsFeature",
                      String.valueOf(featureOID));
    return getSupportedFeatures().contains(featureOID);
  }
}
opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java
@@ -559,10 +559,30 @@
      }
      // Check the set of controls included in the request.  If there are any,
      // see if any special processing is required.  This should also include
      // taking care of any synchronization that might be needed.
      // NYI
      // Look at the controls included in the request and ensure that all
      // critical controls are supported by the handler.
      List<Control> requestControls = getRequestControls();
      if ((requestControls != null) && (! requestControls.isEmpty()))
      {
        for (Control c : requestControls)
        {
          if (! c.isCritical())
          {
            // The control isn't critical, so we don't care if it's supported
            // or not.
          }
          else if (! handler.supportsControl(c.getOID()))
          {
            setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
            int msgID = MSGID_EXTENDED_UNSUPPORTED_CRITICAL_CONTROL;
            appendErrorMessage(getMessage(msgID, String.valueOf(requestOID),
                                          c.getOID()));
            break extendedProcessing;
          }
        }
      }
      // Check to see if the client has permission to perform the
opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperationConfigManager.java
@@ -32,6 +32,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.ConfigAddListener;
@@ -875,6 +876,27 @@
    }
    // If the extended operation handler defines any supported controls and/or
    // features, then register them with the server.
    Set<String> controlOIDs = handler.getSupportedControls();
    if (controlOIDs != null)
    {
      for (String oid : controlOIDs)
      {
        DirectoryServer.registerSupportedControl(oid);
      }
    }
    Set<String> featureOIDs = handler.getSupportedFeatures();
    if (featureOIDs != null)
    {
      for (String oid : featureOIDs)
      {
        DirectoryServer.registerSupportedFeature(oid);
      }
    }
    handlers.put(configEntryDN, handler);
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -34,6 +34,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import org.opends.server.api.ClientConnection;
@@ -45,6 +46,9 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.PasswordPolicyWarningType;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyOperation;
@@ -62,6 +66,7 @@
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.Control;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -107,6 +112,9 @@
  // The reference to the identity mapper.
  private IdentityMapper identityMapper;
  // The set of OIDs for the supported controls.
  private Set<String> supportedControlOIDs;
  /**
@@ -185,6 +193,12 @@
      throw new InitializationException(msgID, message, e);
    }
    supportedControlOIDs = new HashSet<String>();
    supportedControlOIDs.add(OID_LDAP_NOOP_OPENLDAP_ASSIGNED);
    supportedControlOIDs.add(OID_PASSWORD_POLICY_CONTROL);
    DirectoryServer.registerConfigurableComponent(this);
    DirectoryServer.registerSupportedExtension(OID_PASSWORD_MODIFY_REQUEST,
@@ -225,6 +239,30 @@
    ByteString newPassword  = null;
    // Look at the set of controls included in the request, if there are any.
    boolean                   noOpRequested        = false;
    boolean                   pwPolicyRequested    = false;
    int                       pwPolicyWarningValue = 0;
    PasswordPolicyErrorType   pwPolicyErrorType    = null;
    PasswordPolicyWarningType pwPolicyWarningType  = null;
    List<Control> controls = operation.getRequestControls();
    if (controls != null)
    {
      for (Control c : controls)
      {
        String oid = c.getOID();
        if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
        {
          noOpRequested = true;
        }
        else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
        {
          pwPolicyRequested = true;
        }
      }
    }
    // Parse the encoded request, if there is one.
    ByteString requestValue = operation.getRequestValue();
    if (requestValue != null)
@@ -466,6 +504,68 @@
                            userDN.equals(requestorDN));
      // See if the account is locked.  If so, then reject the request.
      if (pwPolicyState.isDisabled())
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType =
               PasswordPolicyErrorType.ACCOUNT_LOCKED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
        int    msgID   = MSGID_EXTOP_PASSMOD_ACCOUNT_DISABLED;
        String message = getMessage(msgID);
        if (oldPassword == null)
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
          operation.appendErrorMessage(message);
        }
        else
        {
          operation.setResultCode(ResultCode.INVALID_CREDENTIALS);
          operation.appendAdditionalLogMessage(message);
        }
        return;
      }
      else if (selfChange &&
               (pwPolicyState.lockedDueToFailures() ||
                pwPolicyState.lockedDueToIdleInterval() ||
                pwPolicyState.lockedDueToMaximumResetAge()))
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType =
               PasswordPolicyErrorType.ACCOUNT_LOCKED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
        int    msgID   = MSGID_EXTOP_PASSMOD_ACCOUNT_LOCKED;
        String message = getMessage(msgID);
        if (oldPassword == null)
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
          operation.appendErrorMessage(message);
        }
        else
        {
          operation.setResultCode(ResultCode.INVALID_CREDENTIALS);
          operation.appendAdditionalLogMessage(message);
        }
        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.
@@ -477,6 +577,17 @@
          int msgID = MSGID_EXTOP_PASSMOD_REQUIRE_CURRENT_PW;
          operation.appendErrorMessage(getMessage(msgID));
          if (pwPolicyRequested)
          {
            pwPolicyErrorType =
                 PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
            operation.addResponseControl(
                 new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                   pwPolicyWarningValue,
                                                   pwPolicyErrorType));
          }
          return;
        }
      }
@@ -507,6 +618,16 @@
      // the request.
      if (selfChange && (! pwPolicyState.allowUserPasswordChanges()))
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType =
               PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
        if (oldPassword == null)
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -554,6 +675,16 @@
      // then reject it.
      if (selfChange && pwPolicyState.isWithinMinimumAge())
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType =
               PasswordPolicyErrorType.PASSWORD_TOO_YOUNG;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
        if (oldPassword == null)
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -578,6 +709,16 @@
      if ((selfChange && pwPolicyState.isPasswordExpired() &&
          (! pwPolicyState.allowExpiredPasswordChanges())))
      {
        if (pwPolicyRequested)
        {
          pwPolicyErrorType =
               PasswordPolicyErrorType.PASSWORD_EXPIRED;
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
        if (oldPassword == null)
        {
          operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -712,6 +853,16 @@
                                                     clearPasswords,
                                                     invalidReason))
            {
              if (pwPolicyRequested)
              {
                pwPolicyErrorType =
                     PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
                operation.addResponseControl(
                     new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                       pwPolicyWarningValue,
                                                       pwPolicyErrorType));
              }
              if (oldPassword == null)
              {
                operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
@@ -923,52 +1074,77 @@
      modList.addAll(pwPolicyState.getModifications());
      // Get an internal connection and use it to perform the modification.
      boolean isRoot = DirectoryServer.isRootDN(requestorDN);
      AuthenticationInfo authInfo = new AuthenticationInfo(requestorDN, isRoot);
      InternalClientConnection internalConnection = new
           InternalClientConnection(authInfo);
      ModifyOperation modifyOperation =
           internalConnection.processModify(userDN, modList);
      ResultCode resultCode = modifyOperation.getResultCode();
      if (resultCode != resultCode.SUCCESS)
      // If the LDAP no-op control was included in the request, then set the
      // appropriate response.  Otherwise, process the operation.
      if (noOpRequested)
      {
        operation.setResultCode(resultCode);
        operation.setErrorMessage(modifyOperation.getErrorMessage());
        operation.setReferralURLs(modifyOperation.getReferralURLs());
        return;
        operation.appendErrorMessage(getMessage(MSGID_EXTOP_PASSMOD_NOOP));
        // FIXME -- We must set a result code other than SUCCESS.
        operation.setResultCode(ResultCode.SUCCESS);
      }
      // If we've gotten here, then everything is OK, so indicate that the
      // operation was successful.  If a password was generated, then include
      // it in the response.
      operation.setResultCode(ResultCode.SUCCESS);
      if (generatedPassword)
      else
      {
        ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(1);
        // Get an internal connection and use it to perform the modification.
        boolean isRoot = DirectoryServer.isRootDN(requestorDN);
        AuthenticationInfo authInfo = new AuthenticationInfo(requestorDN,
                                                             isRoot);
        InternalClientConnection internalConnection = new
             InternalClientConnection(authInfo);
        ASN1OctetString newPWString =
             new ASN1OctetString(TYPE_PASSWORD_MODIFY_GENERATED_PASSWORD,
                                 newPassword.value());
        valueElements.add(newPWString);
        ASN1Sequence valueSequence = new ASN1Sequence(valueElements);
        operation.setResponseValue(new ASN1OctetString(valueSequence.encode()));
      }
        ModifyOperation modifyOperation =
             internalConnection.processModify(userDN, modList);
        ResultCode resultCode = modifyOperation.getResultCode();
        if (resultCode != resultCode.SUCCESS)
        {
          operation.setResultCode(resultCode);
          operation.setErrorMessage(modifyOperation.getErrorMessage());
          operation.setReferralURLs(modifyOperation.getReferralURLs());
          return;
        }
      // 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 we've gotten here, then everything is OK, so indicate that the
        // operation was successful.  If a password was generated, then include
        // it in the response.
        operation.setResultCode(ResultCode.SUCCESS);
        if (generatedPassword)
        {
          ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(1);
          ASN1OctetString newPWString =
               new ASN1OctetString(TYPE_PASSWORD_MODIFY_GENERATED_PASSWORD,
                                   newPassword.value());
          valueElements.add(newPWString);
          ASN1Sequence valueSequence = new ASN1Sequence(valueElements);
          operation.setResponseValue(new ASN1OctetString(
                                              valueSequence.encode()));
        }
        // 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 (pwPolicyRequested)
        {
          operation.addResponseControl(
               new PasswordPolicyResponseControl(pwPolicyWarningType,
                                                 pwPolicyWarningValue,
                                                 pwPolicyErrorType));
        }
      }
    }
    finally
@@ -1056,6 +1232,18 @@
  /**
   * {@inheritDoc}
   */
  public Set<String> getSupportedControls()
  {
    assert debugEnter(CLASS_NAME, "getSupportedControls");
    return supportedControlOIDs;
  }
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5956,6 +5956,17 @@
  /**
   * The message ID for the message that will be used if an extended operation
   * cannot be processed because it contains an unsupported critical control.
   * This takes two arguments, which are the OID of the extended request and the
   * OID of the unsupported control.
   */
  public static final int MSGID_EXTENDED_UNSUPPORTED_CRITICAL_CONTROL =
       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 570;
  /**
   * Associates a set of generic messages with the message IDs defined
   * in this class.
   */
@@ -7198,6 +7209,10 @@
                    "There is no extended operation handler registered with " +
                    "the Directory Server for handling extended operations " +
                    "with a request OID of %s.");
    registerMessage(MSGID_EXTENDED_UNSUPPORTED_CRITICAL_CONTROL,
                    "Unable to process the request for extended operation %s " +
                    "because it contained an unsupported critical control " +
                    "with OID %s.");
    registerMessage(MSGID_CONNHANDLER_CLOSED_BY_SHUTDOWN,
opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4014,6 +4014,34 @@
  /**
   * The message ID for the message that will be used if a password change is
   * not actually performed because the request contained the LDAP no-op
   * control.  This does not take any arguments.
   */
  public static final int MSGID_EXTOP_PASSMOD_NOOP =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_WARNING | 380;
  /**
   * The message ID for the message that will be used if the user's account is
   * disabled.  This does not take any arguments.
   */
  public static final int MSGID_EXTOP_PASSMOD_ACCOUNT_DISABLED =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 381;
  /**
   * The message ID for the message that will be used if the user's account is
   * locked.  This does not take any arguments.
   */
  public static final int MSGID_EXTOP_PASSMOD_ACCOUNT_LOCKED =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 382;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -4274,6 +4302,10 @@
    registerMessage(MSGID_EXTOP_PASSMOD_CANNOT_GET_PW_POLICY,
                    "An error occurred while attempting to get the " +
                    "password policy for user %s:  %s.");
    registerMessage(MSGID_EXTOP_PASSMOD_ACCOUNT_DISABLED,
                    "The user account has been administratively disabled.");
    registerMessage(MSGID_EXTOP_PASSMOD_ACCOUNT_LOCKED,
                    "The user account is locked.");
    registerMessage(MSGID_EXTOP_PASSMOD_REQUIRE_CURRENT_PW,
                    "The current password must be provided for self password " +
                    "changes.");
@@ -4307,6 +4339,10 @@
    registerMessage(MSGID_EXTOP_PASSMOD_CANNOT_ENCODE_PASSWORD,
                    "Unable to encode the provided password using the " +
                    "default scheme(s):  %s.");
    registerMessage(MSGID_EXTOP_PASSMOD_NOOP,
                    "The password modify operation was not actually " +
                    "performed in the Directory Server because the LDAP " +
                    "no-op control was present in the request.");
    registerMessage(MSGID_NULL_KEYMANAGER_NO_MANAGER,
opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -7475,6 +7475,38 @@
  /**
   * The message ID for the message that will be used to display warning
   * information included in the password policy response control.  This takes
   * two arguments, which are the string representation of the warning type and
   * the integer warning value.
   */
  public static final int MSGID_LDAPPWMOD_PWPOLICY_WARNING =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 786;
  /**
   * The message ID for the message that will be used to display error
   * information included in the password policy response control.  This takes
   * a single argument, which is the string representation of the error type.
   */
  public static final int MSGID_LDAPPWMOD_PWPOLICY_ERROR =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 787;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to decode the password policy response control.  This takes a
   * single argument, which is a string representation of the exception that was
   * caught.
   */
  public static final int MSGID_LDAPPWMOD_CANNOT_DECODE_PWPOLICY_CONTROL =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 788;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -9547,6 +9579,13 @@
                    "The LDAP password modify operation was successful.");
    registerMessage(MSGID_LDAPPWMOD_ADDITIONAL_INFO,
                    "Additional Info:  %s.");
    registerMessage(MSGID_LDAPPWMOD_PWPOLICY_WARNING,
                    "Password Policy Warning:  %s = %d.");
    registerMessage(MSGID_LDAPPWMOD_PWPOLICY_ERROR,
                    "Password Policy Error:  %s.");
    registerMessage(MSGID_LDAPPWMOD_CANNOT_DECODE_PWPOLICY_CONTROL,
                    "Unable to decode the password policy response control:  " +
                     "%s.");
    registerMessage(MSGID_LDAPPWMOD_GENERATED_PASSWORD,
                    "Generated Password:  %s.");
    registerMessage(MSGID_LDAPPWMOD_UNRECOGNIZED_VALUE_TYPE,
opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java
@@ -33,6 +33,9 @@
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.PasswordPolicyWarningType;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1Element;
import org.opends.server.protocols.asn1.ASN1OctetString;
@@ -755,6 +758,50 @@
    }
    // See if the response included any controls that we recognize, and if so
    // then handle them.
    ArrayList<LDAPControl> responseControls = responseMessage.getControls();
    if (responseControls != null)
    {
      for (LDAPControl c : responseControls)
      {
        if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
        {
          try
          {
            PasswordPolicyResponseControl pwPolicyControl =
                 PasswordPolicyResponseControl.decodeControl(c.getControl());
            PasswordPolicyWarningType pwPolicyWarningType =
                 pwPolicyControl.getWarningType();
            if (pwPolicyWarningType != null)
            {
              int    msgID   = MSGID_LDAPPWMOD_PWPOLICY_WARNING;
              String message = getMessage(msgID, pwPolicyWarningType.toString(),
                                          pwPolicyControl.getWarningValue());
              out.println(wrapText(message, MAX_LINE_WIDTH));
            }
            PasswordPolicyErrorType pwPolicyErrorType =
                 pwPolicyControl.getErrorType();
            if (pwPolicyErrorType != null)
            {
              int    msgID   = MSGID_LDAPPWMOD_PWPOLICY_ERROR;
              String message = getMessage(msgID, pwPolicyErrorType.toString());
              out.println(wrapText(message, MAX_LINE_WIDTH));
            }
          }
          catch (Exception e)
          {
            int    msgID   = MSGID_LDAPPWMOD_CANNOT_DECODE_PWPOLICY_CONTROL;
            String message = getMessage(msgID, String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
          }
        }
      }
    }
    // See if the response included a generated password.
    ASN1OctetString responseValue = extendedResponse.getValue();
    if (responseValue != null)
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPPasswordModifyTestCase.java
@@ -347,6 +347,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSelfChangeCurrentPasswordNewPassword()
         throws Exception
  {
@@ -393,6 +394,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSelfChangeNoCurrentPasswordNewPassword()
         throws Exception
  {
@@ -438,6 +440,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSelfChangeCurrentPasswordNoNewPassword()
         throws Exception
  {
@@ -483,6 +486,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSelfChangeNoCurrentPasswordNoNewPassword()
         throws Exception
  {
@@ -527,6 +531,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAuthenticatedSelfExplicitAuthzIDCurrentNew()
         throws Exception
  {
@@ -574,6 +579,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAuthenticatedSelfImplicitAuthzIDCurrentNew()
         throws Exception
  {
@@ -622,6 +628,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAuthenticatedSelfImplicitAuthzIDNoCurrentNew()
         throws Exception
  {
@@ -668,6 +675,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testUnauthenticatedSelfChangeNewPassword()
         throws Exception
  {
@@ -713,6 +721,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testUnauthenticatedSelfChangeNoNewPassword()
         throws Exception
  {
@@ -756,6 +765,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAdminResetNewPassword()
         throws Exception
  {
@@ -801,6 +811,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAdminResetNoNewPassword()
         throws Exception
  {
@@ -845,6 +856,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSSLBlindTrust()
         throws Exception
  {
@@ -891,6 +903,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSSLTrustStore()
         throws Exception
  {
@@ -941,6 +954,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testStartTLSBlindTrust()
         throws Exception
  {
@@ -988,6 +1002,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testStartTLSTrustStore()
         throws Exception
  {
@@ -1038,6 +1053,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testBindAndNewPasswordsFromFile()
         throws Exception
  {
@@ -1084,6 +1100,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testCurrentAndNewPasswordsFromFile()
         throws Exception
  {
@@ -1129,6 +1146,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testResetWithInvalidAuthzDN()
         throws Exception
  {
@@ -1156,6 +1174,7 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testResetOnNonExistentUser()
         throws Exception
  {
@@ -1178,6 +1197,252 @@
  /**
   * Tests a failure when attempting an administrative reset on a user entry
   * that has been disabled.  Also include the password policy control in the
   * request.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testResetOnDisabledUser()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry e = TestCaseUtils.makeEntry(
         "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",
         "ds-pwp-account-disabled: true");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    AddOperation addOperation =
         conn.processAdd(e.getDN(), e.getObjectClasses(),
                         e.getUserAttributes(), e.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword",
      "-J", "pwpolicy:true"
    };
    assertFalse(LDAPPasswordModify.mainPasswordModify(args, false, null,
                                                      null) == 0);
  }
  /**
   * Tests the password modify extended operation in conjunction with a control
   * that is marked critical but that is not supported by the server.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLDAPNoOpUnsupportedCriticalControl()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry e = TestCaseUtils.makeEntry(
         "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");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    AddOperation addOperation =
         conn.processAdd(e.getDN(), e.getObjectClasses(),
                         e.getUserAttributes(), e.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword",
      "-J", "1.2.3.4:true"
    };
    assertFalse(LDAPPasswordModify.mainPasswordModify(args, false, null, null)
                == 0);
  }
  /**
   * Tests the password modify extended operation in conjunction with the LDAP
   * no-op control using the explicit OID for that control.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLDAPNoOpExplicitOID()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry e = TestCaseUtils.makeEntry(
         "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");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    AddOperation addOperation =
         conn.processAdd(e.getDN(), e.getObjectClasses(),
                         e.getUserAttributes(), e.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword",
      "-J", OID_LDAP_NOOP_OPENLDAP_ASSIGNED + ":true"
    };
    // FIXME -- Change this whenever the real LDAP No-Op result code is assigned
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
  }
  /**
   * Tests the password modify extended operation in conjunction with the LDAP
   * no-op control using a more user-friendly name instead of an OID.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLDAPNoOpImplicitOID()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry e = TestCaseUtils.makeEntry(
         "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");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    AddOperation addOperation =
         conn.processAdd(e.getDN(), e.getObjectClasses(),
                         e.getUserAttributes(), e.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword",
      "-J", "noop:true"
    };
    // FIXME -- Change this whenever the real LDAP No-Op result code is assigned
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
  }
  /**
   * Tests the password modify extended operation in conjunction with multiple
   * request controls.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLDAPMultipleControls()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    Entry e = TestCaseUtils.makeEntry(
         "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");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    AddOperation addOperation =
         conn.processAdd(e.getDN(), e.getObjectClasses(),
                         e.getUserAttributes(), e.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a", "dn:uid=test.user,o=test",
      "-n", "newPassword",
      "-J", OID_LDAP_NOOP_OPENLDAP_ASSIGNED + ":true",
      "-J", OID_PASSWORD_POLICY_CONTROL + ":true"
    };
    // FIXME -- Change this whenever the real LDAP No-Op result code is assigned
    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
                 0);
  }
  /**
   * Tests the LDAPModify tool with the "--help" option.
   */
  @Test()