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

boli
24.05.2009 90a85f0fed34b0a7d6db93022074a39a17ad27b7
Fix for issue where unit tests hang when the startTLS extended operation is used.
12 files modified
477 ■■■■ changed files
opends/src/server/org/opends/server/api/ClientConnection.java 71 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/BindOperationBasis.java 23 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ExtendedOperation.java 10 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ExtendedOperationBasis.java 56 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/StartTLSExtendedOperation.java 25 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/TLSCapableConnection.java 37 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java 41 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java 34 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java 110 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java 55 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/ClientConnection.java
@@ -38,6 +38,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.opends.messages.Message;
import org.opends.server.api.plugin.PluginResult;
@@ -92,10 +93,19 @@
  // The set of authentication information for this client connection.
  private AuthenticationInfo authenticationInfo;
  // Indicates whether a bind is currently in progress on this client
  // connection.  If so, then no other operations should be allowed
  // until the bind completes.
  private boolean bindInProgress;
  /**
   * Indicates whether a multistage SASL bind is currently in progress
   * on this client connection.  If so, then no other operations
   * should be allowed until the bind completes.
   */
  protected AtomicBoolean saslBindInProgress;
  /**
   * Indicates if a bind or start TLS request is currently in progress
   * on this client connection. If so, then no further socket reads
   * will occur until the request completes.
   */
  protected AtomicBoolean bindOrStartTLSInProgress;
  // Indicates whether any necessary finalization work has been done
  // for this client connection.
@@ -148,7 +158,8 @@
    connectTimeString  = TimeThread.getGMTTime();
    authenticationInfo = new AuthenticationInfo();
    saslAuthState      = null;
    bindInProgress     = false;
    saslBindInProgress = new AtomicBoolean(false);
    bindOrStartTLSInProgress = new AtomicBoolean(false);
    persistentSearches = new CopyOnWriteArrayList<PersistentSearch>();
    sizeLimit          = DirectoryServer.getSizeLimit();
    timeLimit          = DirectoryServer.getTimeLimit();
@@ -695,36 +706,6 @@
  /**
   * Indicates whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed
   * until the bind has completed.
   *
   * @return  {@code true} if a bind operation is in progress on this
   *          connection, or {@code false} if not.
   */
  public boolean bindInProgress()
  {
    return bindInProgress;
  }
  /**
   * Specifies whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed
   * until the bind has completed.
   *
   * @param  bindInProgress  Specifies whether a bind operation is in
   *                         progress on this client connection.
   */
  public void setBindInProgress(boolean bindInProgress)
  {
    this.bindInProgress = bindInProgress;
  }
  /**
   * Indicates whether the user associated with this client connection
   * must change their password before they will be allowed to do
   * anything else.
@@ -1850,5 +1831,25 @@
   * @return An integer representing the SSF value of a connection.
   */
  public abstract int getSSF();
  /**
   * Indicates a bind or start TLS request processing is finished
   * and the client connection may start processing data read from
   * the socket again. This must be called after processing each
   * bind request in a multistage SASL bind.
   */
  public void finishBindOrStartTLS()
  {
    bindOrStartTLSInProgress.set(false);
  }
  /**
   * Indicates a multistage SASL bind operation is finished and the
   * client connection may accept additional LDAP messages.
   */
  public void finishSaslBind()
  {
    saslBindInProgress.set(false);
  }
}
opends/src/server/org/opends/server/core/BindOperationBasis.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import org.opends.messages.Message;
@@ -728,11 +728,6 @@
    ClientConnection clientConnection = getClientConnection();
    // Set a flag to indicate that a bind operation is in progress.  This should
    // ensure that no new operations will be accepted for this client until the
    // bind is complete.
    clientConnection.setBindInProgress(true);
    // Wipe out any existing authentication for the client connection and create
    // a placeholder that will be used if the bind is successful.
    clientConnection.setUnauthenticated();
@@ -838,19 +833,21 @@
    }
    finally
    {
      // If the bind processing is finished, then unset the "bind in progress"
      // flag to allow other operations to be processed on the connection.
      if (getResultCode() != ResultCode.SASL_BIND_IN_PROGRESS)
      {
        clientConnection.setBindInProgress(false);
      }
      // Stop the processing timer.
      setProcessingStopTime();
      // Send the bind response to the client.
      clientConnection.sendResponse(this);
      // If the bind processing is finished, then unset the "bind in progress"
      // flag to allow other operations to be processed on the connection.
      if (getResultCode() != ResultCode.SASL_BIND_IN_PROGRESS)
      {
        clientConnection.finishSaslBind();
      }
      clientConnection.finishBindOrStartTLS();
      // Log the bind response.
      logBindResponse(this);
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -211,7 +211,6 @@
import org.opends.server.types.ObjectClass;
import org.opends.server.types.ObjectClassType;
import org.opends.server.types.OperatingSystem;
import org.opends.server.types.OperationType;
import org.opends.server.types.Privilege;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
@@ -7248,17 +7247,7 @@
  public static void enqueueRequest(AbstractOperation operation)
         throws DirectoryException
  {
    // See if a bind is already in progress on the associated connection.  If so
    // then reject the operation.
    ClientConnection clientConnection = operation.getClientConnection();
    if (clientConnection.bindInProgress() &&
        (operation.getOperationType() != OperationType.BIND))
    {
      Message message = ERR_ENQUEUE_BIND_IN_PROGRESS.get();
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
    }
    //Reject or accept the unauthenticated requests based on the configuration
    // settings.
    if ((directoryServer.rejectUnauthenticatedRequests ||
opends/src/server/org/opends/server/core/ExtendedOperation.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core;
@@ -94,13 +94,5 @@
   *                        the client.
   */
  public void setResponseValue(ByteString responseValue);
  /**
   * Indicates that the response for this extended operation has been sent from
   * somewhere outside of this class.  This should only be used by the StartTLS
   * extended operation for the case in which it needs to send a response in the
   * clear after TLS negotiation has already started on the connection.
   */
  public void setResponseSent();
}
opends/src/server/org/opends/server/core/ExtendedOperationBasis.java
@@ -76,9 +76,6 @@
  // The value for the response associated with this extended operation.
  private ByteString responseValue;
  // Indicates whether a response has yet been sent for this operation.
  private boolean responseSent;
  // The set of response controls for this extended operation.
  private List<Control> responseControls;
@@ -119,7 +116,6 @@
    responseValue    = null;
    responseControls = new ArrayList<Control>();
    cancelRequest    = null;
    responseSent     = false;
    if (requestOID.equals(OID_CANCEL_REQUEST))
    {
@@ -524,17 +520,18 @@
      // Stop the processing timer.
      setProcessingStopTime();
      // Send the response to the client, if it has not already been sent.
      if (! responseSent)
      // Send the response to the client.
      if(cancelRequest == null || cancelResult == null ||
          cancelResult.getResultCode() != ResultCode.CANCELED ||
          cancelRequest.notifyOriginalRequestor() ||
          DirectoryServer.notifyAbandonedOperations())
      {
        responseSent = true;
        if(cancelRequest == null || cancelResult == null ||
            cancelResult.getResultCode() != ResultCode.CANCELED ||
            cancelRequest.notifyOriginalRequestor() ||
            DirectoryServer.notifyAbandonedOperations())
        {
          clientConnection.sendResponse(this);
        }
        clientConnection.sendResponse(this);
      }
      if(requestOID.equals(OID_START_TLS_REQUEST))
      {
        clientConnection.finishBindOrStartTLS();
      }
      // Log the extended response.
@@ -551,37 +548,6 @@
    }
  }
  /**
   * Sends an extended response to the client if none has already been sent.
   * Note that extended operation handlers are strongly discouraged from using
   * this method when it is not necessary because its use will prevent the
   * response from being sent after post-operation plugin processing, which may
   * impact the result that should be included.  Nevertheless, it may be needed
   * in some special cases in which the response must be sent before the
   * extended operation handler completes its processing (e.g., the StartTLS
   * operation in which the response must be sent in the clear before actually
   * enabling TLS protection).
   */
  public final void sendExtendedResponse()
  {
    if (! responseSent)
    {
      responseSent = true;
      clientConnection.sendResponse(this);
    }
  }
  /**
   * {@inheritDoc}
   */
  public final void setResponseSent()
  {
    this.responseSent = true;
  }
  /**
   * {@inheritDoc}
   */
opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
@@ -38,7 +38,7 @@
 */
public class RedirectingByteChannel implements ByteChannel {
    private final ByteChannel child;
    private ByteChannel redirect = null;
    private volatile ByteChannel redirect = null;
    private RedirectingByteChannel(ByteChannel child) {
        this.child = child;
opends/src/server/org/opends/server/extensions/StartTLSExtendedOperation.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
@@ -36,8 +36,6 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
@@ -169,27 +167,6 @@
    // TLS was successfully enabled on the client connection, but we need to
    // send the response in the clear.
    operation.setResultCode(ResultCode.SUCCESS);
    try
    {
      tlsCapableConnection.sendClearResponse(operation);
      operation.setResponseSent();
      tlsCapableConnection.enableTLS();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      logError(ERR_STARTTLS_ERROR_SENDING_CLEAR_RESPONSE.get(
          getExceptionMessage(e)));
      clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
                                  ERR_STARTTLS_ERROR_SENDING_CLEAR_RESPONSE.get(
                                  getExceptionMessage(e)));
    }
  }
}
opends/src/server/org/opends/server/extensions/TLSCapableConnection.java
@@ -22,16 +22,12 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import org.opends.messages.MessageBuilder;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Operation;
/**
 * This interface defines a set of methods that must be implemented by a class
@@ -56,36 +52,5 @@
   *          connection, or <CODE>false</CODE> if it is not.
   */
  public boolean isTLSAvailable(MessageBuilder unavailableReason);
  /**
   * Installs the TLS connection security provider on this client connection.
   * If an error occurs in the process, then the underlying client connection
   * must be terminated and an exception must be thrown to indicate the
   * underlying cause.
   *
   * @throws  DirectoryException  If the TLS connection security provider could
   *                              not be enabled and the underlying connection
   *                              has been closed.
   */
  public void enableTLS()
         throws DirectoryException;
  /**
   * Sends a response to the client in the clear rather than through the
   * encrypted channel.  This should only be used when processing the StartTLS
   * extended operation to send the response in the clear after the SSL
   * negotiation has already been initiated.
   *
   * @param  operation  The operation for which to send the response in the
   *                    clear.
   *
   * @throws  DirectoryException  If a problem occurs while sending the response
   *                              in the clear.
   */
  public void sendClearResponse(Operation operation)
         throws DirectoryException;
}
opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.protocols.internal;
@@ -2746,45 +2746,6 @@
  /**
   * Indicates whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed
   * until the bind has completed.
   *
   * @return  <CODE>true</CODE> if a bind operation is in progress on
   *          this connection, or <CODE>false</CODE> if not.
   */
  @Override()
  public boolean bindInProgress()
  {
    // For internal operations, we don't care if there are any binds
    // in progress.
    return false;
  }
  /**
   * Specifies whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed
   * until the bind has completed.
   *
   * @param  bindInProgress  Specifies whether a bind operation is in
   *                         progress on this client connection.
   */
  @org.opends.server.types.PublicAPI(
       stability=org.opends.server.types.StabilityLevel.PRIVATE,
       mayInstantiate=false,
       mayExtend=false,
       mayInvoke=false)
  @Override()
  public void setBindInProgress(boolean bindInProgress)
  {
    // No implementation is required.
  }
  /**
   * Retrieves the set of operations in progress for this client
   * connection.  This list must not be altered by any caller.
   *
opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.protocols.jmx;
import org.opends.messages.Message;
@@ -1026,38 +1026,6 @@
  /**
   * Indicates whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed until the bind
   * has completed.
   *
   * @return  <CODE>true</CODE> if a bind operation is in progress on this
   *          connection, or <CODE>false</CODE> if not.
   */
  public boolean bindInProgress()
  {
    // For Jmx operations, we don't care if there are any binds in
    // progress.
    return false;
  }
  /**
   * Specifies whether a bind operation is in progress on this client
   * connection.  If so, then no new operations should be allowed until the bind
   * has completed.
   *
   * @param  bindInProgress  Specifies whether a bind operation is in progress
   *                         on this client connection.
   */
  public void setBindInProgress(boolean bindInProgress)
  {
    // No implementation is required.
  }
  /**
   * Retrieves the set of operations in progress for this client connection.
   * This list must not be altered by any caller.
   *
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -50,6 +50,7 @@
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import static org.opends.messages.CoreMessages.ERR_ENQUEUE_BIND_IN_PROGRESS;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.core.AbandonOperationBasis;
@@ -76,24 +77,9 @@
import org.opends.server.protocols.asn1.ASN1;
import org.opends.server.protocols.asn1.ASN1ByteChannelReader;
import org.opends.server.protocols.asn1.ASN1Writer;
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringBuilder;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.IntermediateResponse;
import org.opends.server.types.Operation;
import org.opends.server.types.OperationType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.*;
import org.opends.server.util.TimeThread;
import static org.opends.server.util.ServerConstants.OID_START_TLS_REQUEST;
/**
@@ -200,9 +186,9 @@
  private final RedirectingByteChannel saslChannel;
  private final RedirectingByteChannel tlsChannel;
  private ConnectionSecurityProvider activeProvider = null;
  private ConnectionSecurityProvider tlsPendingProvider = null;
  private ConnectionSecurityProvider saslPendingProvider = null;
  private volatile ConnectionSecurityProvider activeProvider = null;
  private volatile ConnectionSecurityProvider tlsPendingProvider = null;
  private volatile ConnectionSecurityProvider saslPendingProvider = null;
  // Statistics for the processed operations
  private OperationMonitor addMonitor;
@@ -1419,12 +1405,15 @@
   */
  public boolean processDataRead()
  {
    if (this.saslPendingProvider != null)
    {
      enableSASL();
    }
    while (true)
    {
      if(bindOrStartTLSInProgress.get())
      {
        // We should wait for the bind or startTLS to finish before
        // reading any more data off the socket.
        return true;
      }
      try
      {
        int result = asn1Reader.processChannelData();
@@ -1541,6 +1530,14 @@
    // terminated.
    try
    {
      if(bindOrStartTLSInProgress.get() ||
          (saslBindInProgress.get() &&
              message.getProtocolOpType() != OP_TYPE_BIND_REQUEST))
      {
        throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
            ERR_ENQUEUE_BIND_IN_PROGRESS.get());
      }
      boolean result;
      switch (message.getProtocolOpType())
      {
@@ -1564,7 +1561,22 @@
        return result;
      case OP_TYPE_BIND_REQUEST:
        if (keepStats) this.bindMonitor.start();
        bindOrStartTLSInProgress.set(true);
        if(message.getBindRequestProtocolOp().
            getAuthenticationType() == AuthenticationType.SASL)
        {
          saslBindInProgress.set(true);
        }
        result = processBindRequest(message, opControls);
        if(!result)
        {
          bindOrStartTLSInProgress.set(false);
          if(message.getBindRequestProtocolOp().
              getAuthenticationType() == AuthenticationType.SASL)
          {
            saslBindInProgress.set(false);
          }
        }
        if (keepStats)
        {
          this.bindMonitor.stop();
@@ -1591,7 +1603,18 @@
        return result;
      case OP_TYPE_EXTENDED_REQUEST:
        if (keepStats) this.extendedMonitor.start();
        if(message.getExtendedRequestProtocolOp().getOID().equals(
            OID_START_TLS_REQUEST))
        {
          bindOrStartTLSInProgress.set(true);
        }
        result = processExtendedRequest(message, opControls);
        if(!result &&
            message.getExtendedRequestProtocolOp().getOID().equals(
                OID_START_TLS_REQUEST))
        {
          bindOrStartTLSInProgress.set(false);
        }
        if (keepStats)
        {
          this.extendedMonitor.stop();
@@ -2447,26 +2470,6 @@
  /**
   * Sends a response to the client in the clear rather than through the
   * encrypted channel. This should only be used when processing the
   * StartTLS extended operation to send the response in the clear after
   * the TLS negotiation has already been initiated.
   *
   * @param operation
   *          The operation for which to send the response in the clear.
   * @throws DirectoryException
   *           If a problem occurs while sending the response in the
   *           clear.
   */
  public void sendClearResponse(Operation operation)
      throws DirectoryException
  {
    sendLDAPMessage(operationToResponseLDAPMessage(operation));
  }
  /**
   * Retrieves the length of time in milliseconds that this client
   * connection has been idle. <BR>
   * <BR>
@@ -2649,4 +2652,23 @@
    this.moddnMonitor = OperationMonitor.getOperationMonitor(MODIFY_DN);
    this.unbindMonitor = OperationMonitor.getOperationMonitor(UNBIND);
  }
  /**
   * {@inheritDoc}
   */
  public void finishBindOrStartTLS()
  {
    if(this.tlsPendingProvider != null)
    {
      enableTLS();
    }
    if (this.saslPendingProvider != null)
    {
      enableSASL();
    }
    super.finishBindOrStartTLS();
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.protocols.internal;
@@ -1010,59 +1010,6 @@
  /**
   * Tests the <CODE>bindInProgress</CODE> method.
   */
  @Test()
  public void testBindInProgress()
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    assertFalse(conn.bindInProgress());
  }
  /**
   * Tests the <CODE>setBindInProgress</CODE> method.
   */
  @Test()
  public void testSetBindInProgress()
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    conn.setBindInProgress(true);
    assertFalse(conn.bindInProgress());
  }
  /**
   * Tests the <CODE>getOperationsInProgress</CODE> method.
   */
  @Test()
  public void testGetOperationsInProgress()
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    Collection<Operation> opList = conn.getOperationsInProgress();
    assertNotNull(opList);
    assertTrue(opList.isEmpty());
  }
  /**
   * Tests the <CODE>getOperationInProgress</CODE> method.
   */
  @Test()
  public void testGetOperationInProgress()
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    assertNull(conn.getOperationInProgress(0));
  }
  /**