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

Jean-Noel Rouvignac
27.29.2014 4cca8e97f18e39834d50177899b5f5ac2ebaeed2
opends/src/server/org/opends/server/core/AuthenticatedUsers.java
@@ -22,32 +22,29 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2014 ForgeRock AS
 */
package org.opends.server.core;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.opends.messages.Message;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.messages.Message;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.DITCacheMap;
import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
import org.opends.server.api.plugin.PluginResult.PostResponse;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Entry;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.*;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.api.plugin.PluginType.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
/**
@@ -60,34 +57,52 @@
 * user entries and notifying the corresponding client connections so that they
 * can update their cached versions.
 */
public class AuthenticatedUsers
       implements ChangeNotificationListener
public class AuthenticatedUsers extends InternalDirectoryServerPlugin
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // The mapping between authenticated user DNs and the associated client
  // connection objects.
  private DITCacheMap<CopyOnWriteArraySet<ClientConnection>> userMap;
  /**
   * The mapping between authenticated user DNs and the associated client
   * connection objects.
   */
  private final DITCacheMap<CopyOnWriteArraySet<ClientConnection>> userMap;
  // Lock to protect internal data structures.
  /** Lock to protect internal data structures. */
  private final ReentrantReadWriteLock lock;
  /** Dummy configuration DN. */
  private static final String CONFIG_DN = "cn=Authenticated Users,cn=config";
  /**
   * Creates a new instance of this authenticated users object.
   */
  public AuthenticatedUsers()
  {
    super(toDN(CONFIG_DN), EnumSet.of(
        // No implementation is required for add operations, since a connection
        // can not be authenticated as a user that does not exist yet.
        POST_RESPONSE_MODIFY, POST_RESPONSE_MODIFY_DN, POST_RESPONSE_DELETE),
        true);
    userMap = new DITCacheMap<CopyOnWriteArraySet<ClientConnection>>();
    lock = new ReentrantReadWriteLock();
    DirectoryServer.registerChangeNotificationListener(this);
    DirectoryServer.registerInternalPlugin(this);
  }
  private static DN toDN(String dn)
  {
    try
    {
      return DN.decode(dn);
    }
    catch (DirectoryException e)
    {
      throw new RuntimeException(e);
    }
  }
  /**
   * Registers the provided user DN and client connection with this object.
@@ -102,8 +117,7 @@
    lock.writeLock().lock();
    try
    {
      CopyOnWriteArraySet<ClientConnection> connectionSet =
              userMap.get(userDN);
      CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
      if (connectionSet == null)
      {
        connectionSet = new CopyOnWriteArraySet<ClientConnection>();
@@ -136,8 +150,7 @@
    lock.writeLock().lock();
    try
    {
      CopyOnWriteArraySet<ClientConnection> connectionSet =
              userMap.get(userDN);
      CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
      if (connectionSet != null)
      {
        connectionSet.remove(clientConnection);
@@ -179,89 +192,59 @@
    }
  }
  /**
   * Performs any processing that may be required after an add
   * operation.
   *
   * @param  addOperation  The add operation that was performed in the
   *                       server.
   * @param  entry         The entry that was added to the server.
   */
  public void handleAddOperation(
                   PostResponseAddOperation addOperation,
                   Entry entry)
  /** {@inheritDoc} */
  @Override
  public PostResponse doPostResponse(PostResponseDeleteOperation op)
  {
    // No implementation is required for add operations, since a connection
    // can't be authenticated as a user that doesn't exist yet.
  }
    if (op.getResultCode() != ResultCode.SUCCESS) {
      return PostResponse.continueOperationProcessing();
    }
  /**
   * Performs any processing that may be required after a delete
   * operation.
   *
   * @param  deleteOperation  The delete operation that was performed
   *                          in the server.
   * @param  entry            The entry that was removed from the
   *                          server.
   */
  public void handleDeleteOperation(
                   PostResponseDeleteOperation deleteOperation,
                   Entry entry)
  {
    final DN entryDN = op.getEntryDN();
    // Identify any client connections that may be authenticated
    // or authorized as the user whose entry has been deleted and
    // terminate them.
    // or authorized as the user whose entry has been deleted and terminate them
    Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
            new HashSet<CopyOnWriteArraySet<ClientConnection>>();
    lock.writeLock().lock();
    try
    {
      userMap.removeSubtree(entry.getDN(), arraySet);
      userMap.removeSubtree(entryDN, arraySet);
    }
    finally
    {
      lock.writeLock().unlock();
    }
    for (CopyOnWriteArraySet<ClientConnection>
            connectionSet : arraySet)
    for (CopyOnWriteArraySet<ClientConnection> connectionSet : arraySet)
    {
      for (ClientConnection conn : connectionSet)
      {
        Message message = WARN_CLIENTCONNECTION_DISCONNECT_DUE_TO_DELETE.get(
                String.valueOf(entry.getDN()));
                String.valueOf(entryDN));
        conn.disconnect(DisconnectReason.INVALID_CREDENTIALS, true, message);
      }
    }
    return PostResponse.continueOperationProcessing();
  }
  /**
   * Performs any processing that may be required after a modify
   * operation.
   *
   * @param  modifyOperation  The modify operation that was performed
   *                          in the server.
   * @param  oldEntry         The entry before it was updated.
   * @param  newEntry         The entry after it was updated.
   */
  public void handleModifyOperation(
                   PostResponseModifyOperation modifyOperation,
                   Entry oldEntry, Entry newEntry)
  /** {@inheritDoc} */
  @Override
  public PostResponse doPostResponse(PostResponseModifyOperation op)
  {
    if (op.getResultCode() != ResultCode.SUCCESS) {
      return PostResponse.continueOperationProcessing();
    }
    final Entry oldEntry = op.getCurrentEntry();
    final Entry newEntry = op.getModifiedEntry();
    // Identify any client connections that may be authenticated
    // or authorized as the user whose entry has been modified
    // and update them with the latest version of the entry.
    lock.writeLock().lock();
    try
    {
      CopyOnWriteArraySet<ClientConnection> connectionSet =
           userMap.get(oldEntry.getDN());
      CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(oldEntry.getDN());
      if (connectionSet != null)
      {
        for (ClientConnection conn : connectionSet)
@@ -274,23 +257,19 @@
    {
      lock.writeLock().unlock();
    }
    return PostResponse.continueOperationProcessing();
  }
  /**
   * Performs any processing that may be required after a modify DN
   * operation.
   *
   * @param  modifyDNOperation  The modify DN operation that was
   *                            performed in the server.
   * @param  oldEntry           The entry before it was updated.
   * @param  newEntry           The entry after it was updated.
   */
  public void handleModifyDNOperation(
                   PostResponseModifyDNOperation modifyDNOperation,
                   Entry oldEntry, Entry newEntry)
  /** {@inheritDoc} */
  @Override
  public PostResponse doPostResponse(PostResponseModifyDNOperation op)
  {
    if (op.getResultCode() != ResultCode.SUCCESS) {
      return PostResponse.continueOperationProcessing();
    }
    Entry oldEntry = op.getOriginalEntry();
    Entry newEntry = op.getUpdatedEntry();
    String oldDNString = oldEntry.getDN().toNormalizedString();
    String newDNString = newEntry.getDN().toNormalizedString();
@@ -303,8 +282,7 @@
      Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
        new HashSet<CopyOnWriteArraySet<ClientConnection>>();
      userMap.removeSubtree(oldEntry.getDN(), arraySet);
      for (CopyOnWriteArraySet<ClientConnection>
              connectionSet : arraySet)
      for (CopyOnWriteArraySet<ClientConnection> connectionSet : arraySet)
      {
        DN authNDN = null;
        DN authZDN = null;
@@ -319,17 +297,11 @@
            authNDN = conn.getAuthenticationInfo().getAuthenticationDN();
            try
            {
              StringBuilder builder = new StringBuilder(
                  authNDN.toNormalizedString());
              int oldDNIndex = builder.lastIndexOf(oldDNString);
              builder.replace(oldDNIndex, builder.length(),
                      newDNString);
              String newAuthNDNString = builder.toString();
              newAuthNDN = DN.decode(newAuthNDNString);
              newAuthNDN = getNewAuthDN(authNDN, oldDNString, newDNString);
            }
            catch (Exception e)
            {
              // Shouldnt happen.
              // Should not happen.
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -341,24 +313,18 @@
            authZDN = conn.getAuthenticationInfo().getAuthorizationDN();
            try
            {
              StringBuilder builder = new StringBuilder(
                  authZDN.toNormalizedString());
              int oldDNIndex = builder.lastIndexOf(oldDNString);
              builder.replace(oldDNIndex, builder.length(),
                      newDNString);
              String newAuthZDNString = builder.toString();
              newAuthZDN = DN.decode(newAuthZDNString);
              newAuthZDN = getNewAuthDN(authZDN, oldDNString, newDNString);
            }
            catch (Exception e)
            {
              // Shouldnt happen.
              // Should not happen.
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
              }
            }
          }
          if ((newAuthNDN != null) && (authNDN != null) &&
          if (newAuthNDN != null && authNDN != null &&
               authNDN.isDescendantOf(oldEntry.getDN()))
          {
            if (newAuthNSet == null)
@@ -368,7 +334,7 @@
            conn.getAuthenticationInfo().setAuthenticationDN(newAuthNDN);
            newAuthNSet.add(conn);
          }
          if ((newAuthZDN != null) && (authZDN != null) &&
          if (newAuthZDN != null && authZDN != null &&
               authZDN.isDescendantOf(oldEntry.getDN()))
          {
            if (newAuthZSet == null)
@@ -379,11 +345,11 @@
            newAuthZSet.add(conn);
          }
        }
        if ((newAuthNDN != null) && (newAuthNSet != null))
        if (newAuthNDN != null && newAuthNSet != null)
        {
          userMap.put(newAuthNDN, newAuthNSet);
        }
        if ((newAuthZDN != null) && (newAuthZSet != null))
        if (newAuthZDN != null && newAuthZSet != null)
        {
          userMap.put(newAuthZDN, newAuthZSet);
        }
@@ -393,6 +359,18 @@
    {
      lock.writeLock().unlock();
    }
    return PostResponse.continueOperationProcessing();
  }
  private DN getNewAuthDN(DN authDN, String oldDNString, String newDNString) throws DirectoryException
  {
    // FIXME once we move to the SDK:
    // Matt suggests we should be using the following code here:
    // return authDN.rename(oldDNString, newDNString);
    final StringBuilder builder = new StringBuilder(authDN.toNormalizedString());
    final int oldDNIndex = builder.lastIndexOf(oldDNString);
    builder.replace(oldDNIndex, builder.length(), newDNString);
    return DN.decode(builder.toString());
  }
}