From 4a4a8540f0b64feff6934c3215c6f896c9561c7d Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 08 Feb 2007 22:27:58 +0000
Subject: [PATCH] Update the AuthenticationInfo object to store the entries for the authentication and authorization identities rather than just their DNs.  This includes a mechanism to keep those entries up to date as changes occur in the server, and also includes a hook for ClientConnection subclasses to perform processing whenever a connection is terminated.

---
 opends/src/server/org/opends/server/core/SearchOperation.java                                                        |   62 +-
 opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java                                              |    6 
 opends/src/server/org/opends/server/messages/CoreMessages.java                                                       |   16 
 opends/src/server/org/opends/server/util/ServerConstants.java                                                        |   15 
 opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV1ControlTestCase.java               |    8 
 opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java                                      |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java                           |    6 
 opends/src/server/org/opends/server/core/Operation.java                                                              |   91 ++-
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java |   21 
 opends/src/server/org/opends/server/core/AddOperation.java                                                           |   12 
 opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java                                       |    6 
 opends/src/server/org/opends/server/core/ModifyOperation.java                                                        |   12 
 opends/src/server/org/opends/server/core/BindOperation.java                                                          |   35 
 opends/src/server/org/opends/server/core/AuthenticatedUsers.java                                                     |  265 +++++++++++
 opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java                                         |    3 
 opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java                                               |   36 
 opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java                                    |    2 
 opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java                                           |    4 
 opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java                                               |   43 +
 opends/src/server/org/opends/server/core/DirectoryServer.java                                                        |   18 
 opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java                                  |   31 
 opends/src/server/org/opends/server/api/ClientConnection.java                                                        |  201 ++++++++
 opends/src/server/org/opends/server/core/CompareOperation.java                                                       |   14 
 opends/src/server/org/opends/server/core/ModifyDNOperation.java                                                      |   12 
 opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java                                                  |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV2ControlTestCase.java               |   20 
 opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java                                        |    2 
 opends/src/server/org/opends/server/types/AuthenticationInfo.java                                                    |  356 +++++++++-----
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java          |    5 
 opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java                                 |   72 ++
 opends/src/server/org/opends/server/core/DeleteOperation.java                                                        |   12 
 opends/src/server/org/opends/server/extensions/ExternalSASLMechanismHandler.java                                     |    2 
 32 files changed, 1,054 insertions(+), 338 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index 4bfd6e6..c113d31 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -80,6 +80,10 @@
   // until the bind completes.
   private boolean bindInProgress;
 
+  // Indicates whether any necessary finalization work has been done
+  // for this client connection.
+  private boolean finalized;
+
   // The size limit for use with this client connection.
   private int sizeLimit;
 
@@ -122,6 +126,79 @@
     sizeLimit          = DirectoryServer.getSizeLimit();
     timeLimit          = DirectoryServer.getTimeLimit();
     lookthroughLimit   = DirectoryServer.getLookthroughLimit();
+    finalized          = false;
+  }
+
+
+
+  /**
+   * Performs any internal cleanup that may be necessary when this
+   * client connection is disconnected, or if not on disconnec, then
+   * ultimately whenever it is reaped by the garbage collector.  In
+   * this case, it will be used to ensure that the connection is
+   * deregistered with the {@code AuthenticatedUsers} manager, and
+   * will then invoke the {@code finalizeClientConnection} method.
+   */
+  protected final void finalizeConnectionInternal()
+  {
+    if (finalized)
+    {
+      return;
+    }
+
+    finalized = true;
+
+    // Deregister with the set of authenticated users.
+    Entry authNEntry = authenticationInfo.getAuthenticationEntry();
+    Entry authZEntry = authenticationInfo.getAuthorizationEntry();
+
+    if (authNEntry != null)
+    {
+      if ((authZEntry == null) ||
+          authZEntry.getDN().equals(authNEntry.getDN()))
+      {
+        DirectoryServer.getAuthenticatedUsers().remove(
+             authNEntry.getDN(), this);
+      }
+      else
+      {
+        DirectoryServer.getAuthenticatedUsers().remove(
+             authNEntry.getDN(), this);
+        DirectoryServer.getAuthenticatedUsers().remove(
+             authZEntry.getDN(), this);
+      }
+    }
+    else if (authZEntry != null)
+    {
+      DirectoryServer.getAuthenticatedUsers().remove(
+           authZEntry.getDN(), this);
+    }
+
+    try
+    {
+      finalizeClientConnection();
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "finalizeConnectionInternal",
+                            e);
+    }
+  }
+
+
+
+  /**
+   * Performs any cleanup work that may be necessary when this client
+   * connection is terminated.  By default, no action is taken.
+   * <BR><BR>
+   * If possible, this method will be invoked when the client
+   * connection is disconnected.  If it isn't invoked at that time,
+   * then it will be called when the client connection object is
+   * finalized by the garbage collector.
+   */
+  protected void finalizeClientConnection()
+  {
+    // No implementation is required by default.
   }
 
 
@@ -430,9 +507,9 @@
    *                           associated with the provided message
    *                           ID.
    */
-  public void disconnect(DisconnectReason disconnectReason,
-                         boolean sendNotification, int messageID,
-                         Object... arguments)
+  public final void disconnect(DisconnectReason disconnectReason,
+                               boolean sendNotification,
+                               int messageID, Object... arguments)
   {
     assert debugEnter(CLASS_NAME, "disconnect",
                       String.valueOf(disconnectReason),
@@ -456,6 +533,9 @@
    * operation processing (e.g., within a plugin or other extension),
    * the <CODE>disconnectClient</CODE> method within that operation
    * should be called rather than invoking this method directly.
+   * <BR><BR>
+   * All subclasses must invoke the {@code finalizeConnectionInternal}
+   * method during the course of processing this method.
    *
    * @param  disconnectReason  The disconnect reason that provides the
    *                           generic cause for the disconnect.
@@ -733,6 +813,36 @@
     assert debugEnter(CLASS_NAME, "setAuthenticationInfo",
                       String.valueOf(authenticationInfo));
 
+    if (this.authenticationInfo != null)
+    {
+      Entry authNEntry =
+                 this.authenticationInfo.getAuthenticationEntry();
+      Entry authZEntry =
+                 this.authenticationInfo.getAuthorizationEntry();
+
+      if (authNEntry != null)
+      {
+        if ((authZEntry == null) ||
+            authZEntry.getDN().equals(authNEntry.getDN()))
+        {
+          DirectoryServer.getAuthenticatedUsers().remove(
+               authNEntry.getDN(), this);
+        }
+        else
+        {
+          DirectoryServer.getAuthenticatedUsers().remove(
+               authNEntry.getDN(), this);
+          DirectoryServer.getAuthenticatedUsers().remove(
+               authZEntry.getDN(), this);
+        }
+      }
+      else if (authZEntry != null)
+      {
+        DirectoryServer.getAuthenticatedUsers().remove(
+             authZEntry.getDN(), this);
+      }
+    }
+
     if (authenticationInfo == null)
     {
       this.authenticationInfo = new AuthenticationInfo();
@@ -740,6 +850,76 @@
     else
     {
       this.authenticationInfo = authenticationInfo;
+
+      Entry authNEntry = authenticationInfo.getAuthenticationEntry();
+      Entry authZEntry = authenticationInfo.getAuthorizationEntry();
+
+      if (authNEntry != null)
+      {
+        if ((authZEntry == null) ||
+            authZEntry.getDN().equals(authNEntry.getDN()))
+        {
+          DirectoryServer.getAuthenticatedUsers().put(
+               authNEntry.getDN(), this);
+        }
+        else
+        {
+          DirectoryServer.getAuthenticatedUsers().put(
+               authNEntry.getDN(), this);
+          DirectoryServer.getAuthenticatedUsers().put(
+               authZEntry.getDN(), this);
+        }
+      }
+      else if (authZEntry != null)
+      {
+        DirectoryServer.getAuthenticatedUsers().put(
+             authZEntry.getDN(), this);
+      }
+    }
+  }
+
+
+
+  /**
+   * Updates the cached entry associated with either the
+   * authentication and/or authorization identity with the provided
+   * version.
+   *
+   * @param  oldEntry  The user entry currently serving as the
+   *                   authentication and/or authorization identity.
+   * @param  newEntry  The updated entry that should replace the
+   *                   existing entry.  It may optionally have a
+   *                   different DN than the old entry.
+   */
+  public void updateAuthenticationInfo(Entry oldEntry, Entry newEntry)
+  {
+    assert debugEnter(CLASS_NAME, "updateAuthenticationInfo",
+                      String.valueOf(oldEntry),
+                      String.valueOf(newEntry));
+
+    Entry authNEntry = authenticationInfo.getAuthenticationEntry();
+    Entry authZEntry = authenticationInfo.getAuthorizationEntry();
+
+    if ((authNEntry != null) &&
+        authNEntry.getDN().equals(oldEntry.getDN()))
+    {
+      if ((authZEntry == null) ||
+          (! authZEntry.getDN().equals(authNEntry.getDN())))
+      {
+        authenticationInfo =
+             authenticationInfo.duplicate(newEntry, authZEntry);
+      }
+      else
+      {
+        authenticationInfo =
+             authenticationInfo.duplicate(newEntry, newEntry);
+      }
+    }
+    else if ((authZEntry != null) &&
+             (authZEntry.getDN().equals(oldEntry.getDN())))
+    {
+      authenticationInfo =
+           authenticationInfo.duplicate(authNEntry, newEntry);
     }
   }
 
@@ -1053,5 +1233,20 @@
    *                 appended.
    */
   public abstract void toString(StringBuilder buffer);
+
+
+
+  /**
+   * Performs any work that may be needed before the JVM invokes
+   * garbage collection for this object.  In this case, it makes sure
+   * to deregister with the Directory Server as a change notification
+   * listener.  If a subclass wishes to perform custom finalization
+   * processing, then it should override this method and make sure to
+   * invoke {@code super.finalize} as its first call.
+   */
+  protected void finalize()
+  {
+    finalizeConnectionInternal();
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java b/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
index 518d547..4af026e 100644
--- a/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
+++ b/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
@@ -327,27 +327,37 @@
 
 
   /**
-   * Retrieves the authorization DN for this proxied authorization V1 control
-   * only if it references a valid Directory Server user entry.  It will also
-   * perform any necessary password policy checks to ensure that the specified
-   * user account is suitable for use in performing this processing.
+   * Retrieves the authorization entry for this proxied authorization V1
+   * control.  It will also perform any necessary password policy checks to
+   * ensure that the associated user account is suitable for use in performing
+   * this processing.
    *
-   * @return  The validated authorization DN for this proxied authorization V1
-   *          control.
+   * @return  The entry for user specified as the authorization identity in this
+   *          proxied authorization V1 control, or {@code null} if the
+   *          authorization DN is the null DN.
    *
-   * @throws  DirectoryException  If an error occurs while attempting to make
-   *                              the determination, or if the target user does
-   *                              not exist.
+   * @throws  DirectoryException  If the target user does not exist or is not
+   *                              available for use, or if a problem occurs
+   *                              while making the determination.
    */
-  public DN getValidatedAuthorizationDN()
+  public Entry getAuthorizationEntry()
          throws DirectoryException
   {
-    assert debugEnter(CLASS_NAME, "getValidatedAuthorizationDN");
+    assert debugEnter(CLASS_NAME, "getAuthorizationEntry");
 
     DN authzDN = getAuthorizationDN();
     if (authzDN.isNullDN())
     {
-      return authzDN;
+      return null;
+    }
+
+
+    // See if the authorization DN is one of the alternate bind DNs for one of
+    // the root users and if so then map it accordingly.
+    DN actualDN = DirectoryServer.getActualRootBindDN(authzDN);
+    if (actualDN != null)
+    {
+      authzDN = actualDN;
     }
 
 
@@ -400,7 +410,7 @@
 
 
       // If we've made it here, then the user is acceptable.
-      return authzDN;
+      return userEntry;
     }
     finally
     {
diff --git a/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java b/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
index 07f993d..068151e 100644
--- a/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
+++ b/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
@@ -233,19 +233,20 @@
 
 
   /**
-   * Retrieves the authorization DN for this proxied authorization V2 control
-   * only if it references a valid Directory Server user entry.  It will also
-   * perform any necessary password policy checks to ensure that the specified
-   * user account is suitable for use in performing this processing.
+   * Retrieves the authorization entry for this proxied authorization V2
+   * control.  It will also perform any necessary password policy checks to
+   * ensure that the associated user account is suitable for use in performing
+   * this processing.
    *
-   * @return  The validated authorization DN for this proxied authorization V2
-   *          control.
+   * @return  The entry for user specified as the authorization identity in this
+   *          proxied authorization V1 control, or {@code null} if the
+   *          authorization DN is the null DN.
    *
-   * @throws  DirectoryException  If an error occurs while attempting to make
-   *                              the determination, or if the target user does
-   *                              not exist.
+   * @throws  DirectoryException  If the target user does not exist or is not
+   *                              available for use, or if a problem occurs
+   *                              while making the determination.
    */
-  public DN getValidatedAuthorizationDN()
+  public Entry getAuthorizationEntry()
          throws DirectoryException
   {
     assert debugEnter(CLASS_NAME, "getValidatedAuthorizationDN");
@@ -254,7 +255,7 @@
     // Check for a zero-length value, which would be for an anonymous user.
     if (authorizationID.value().length == 0)
     {
-      return DN.nullDN();
+      return null;
     }
 
 
@@ -269,10 +270,18 @@
       DN authzDN = DN.decode(authzID.substring(3));
       if (authzDN.isNullDN())
       {
-        return authzDN;
+        return null;
       }
       else
       {
+        // See if the authorization DN is one of the alternate bind DNs for one
+        // of the root users and if so then map it accordingly.
+        DN actualDN = DirectoryServer.getActualRootBindDN(authzDN);
+        if (actualDN != null)
+        {
+          authzDN = actualDN;
+        }
+
         Lock entryLock = null;
         for (int i=0; i < 3; i++)
         {
@@ -321,7 +330,7 @@
 
 
           // If we've made it here, then the user is acceptable.
-          return authzDN;
+          return userEntry;
         }
         finally
         {
@@ -334,7 +343,7 @@
       // If the authorization ID is just "u:", then it's an anonymous request.
       if (lowerAuthzID.length() == 2)
       {
-        return DN.nullDN();
+        return null;
       }
 
 
@@ -360,8 +369,6 @@
       }
       else
       {
-        DN authzDN = userEntry.getDN();
-
         // FIXME -- We should provide some mechanism for enabling debug
         // processing.
         PasswordPolicyState pwpState =
@@ -373,12 +380,12 @@
             pwpState.isPasswordExpired())
         {
           int    msgID   = MSGID_PROXYAUTH2_UNUSABLE_ACCOUNT;
-          String message = getMessage(msgID, String.valueOf(authzDN));
+          String message = getMessage(msgID, String.valueOf(userEntry.getDN()));
           throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                                        message, msgID);
         }
 
-        return authzDN;
+        return userEntry;
       }
     }
     else
diff --git a/opends/src/server/org/opends/server/core/AddOperation.java b/opends/src/server/org/opends/server/core/AddOperation.java
index 2e4b8bd..0f3e85b 100644
--- a/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opends/src/server/org/opends/server/core/AddOperation.java
@@ -1783,10 +1783,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1801,7 +1801,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
             else if (oid.equals(OID_PROXIED_AUTH_V2))
             {
@@ -1828,10 +1828,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1846,7 +1846,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
 
             // NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/core/AuthenticatedUsers.java b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
new file mode 100644
index 0000000..5560f2d
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
@@ -0,0 +1,265 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.types.DisconnectReason;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.operation.PostResponseAddOperation;
+import org.opends.server.types.operation.PostResponseDeleteOperation;
+import org.opends.server.types.operation.PostResponseModifyOperation;
+import org.opends.server.types.operation.PostResponseModifyDNOperation;
+
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+
+
+/**
+ * This class provides a data structure which maps an authenticated user DN to
+ * the set of client connections authenticated as that user.  Note that a single
+ * client connection may be registered with two different user DNs if the client
+ * has different authentication and authorization identities.
+ * <BR><BR>
+ * This class also provides a mechanism for detecting changes to authenticated
+ * user entries and notifying the corresponding client connections so that they
+ * can update their cached versions.
+ */
+public class AuthenticatedUsers
+       implements ChangeNotificationListener
+{
+  /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+       "org.opends.server.core.AuthenticatedUsers";
+
+
+
+  // The mapping between authenticated user DNs and the associated client
+  // connection objects.
+  private ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>
+               userMap;
+
+
+
+  /**
+   * Creates a new instance of this authenticated users object.
+   */
+  public AuthenticatedUsers()
+  {
+    assert debugConstructor(CLASS_NAME);
+
+    userMap = new ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>();
+
+    DirectoryServer.registerChangeNotificationListener(this);
+  }
+
+
+
+  /**
+   * Registers the provided user DN and client connection with this object.
+   *
+   * @param  userDN            The DN of the user associated with the provided
+   *                           client connection.
+   * @param  clientConnection  The client connection over which the user is
+   *                           authenticated.
+   */
+  public synchronized void put(DN userDN, ClientConnection clientConnection)
+  {
+    assert debugEnter(CLASS_NAME, "put", String.valueOf(userDN),
+                      String.valueOf(clientConnection));
+
+    CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
+    if (connectionSet == null)
+    {
+      connectionSet = new CopyOnWriteArraySet<ClientConnection>();
+      connectionSet.add(clientConnection);
+      userMap.put(userDN, connectionSet);
+    }
+    else
+    {
+      connectionSet.add(clientConnection);
+    }
+  }
+
+
+
+  /**
+   * Deregisters the provided user DN and client connection with this object.
+   *
+   * @param  userDN            The DN of the user associated with the provided
+   *                           client connection.
+   * @param  clientConnection  The client connection over which the user is
+   *                           authenticated.
+   */
+  public synchronized void remove(DN userDN, ClientConnection clientConnection)
+  {
+    assert debugEnter(CLASS_NAME, "put", String.valueOf(userDN),
+                      String.valueOf(clientConnection));
+
+    CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
+    if (connectionSet != null)
+    {
+      connectionSet.remove(clientConnection);
+      if (connectionSet.isEmpty())
+      {
+        userMap.remove(userDN);
+      }
+    }
+  }
+
+
+
+  /**
+   * 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)
+  {
+    // No implementation is required for add operations, since a connection
+    // can't be authenticated as a user that doesn't exist yet.
+  }
+
+
+
+  /**
+   * 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)
+  {
+    // Identify any client connections that may be authenticated or
+    // authorized as the user whose entry has been deleted and terminate them.
+    CopyOnWriteArraySet<ClientConnection> connectionSet =
+         userMap.remove(entry.getDN());
+    if (connectionSet != null)
+    {
+      for (ClientConnection conn : connectionSet)
+      {
+        int    msgID   = MSGID_CLIENTCONNECTION_DISCONNECT_DUE_TO_DELETE;
+        String message = getMessage(msgID, String.valueOf(entry.getDN()));
+
+        conn.disconnect(DisconnectReason.OTHER, true, message, msgID);
+      }
+    }
+  }
+
+
+
+  /**
+   * 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)
+  {
+    // 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.
+    CopyOnWriteArraySet<ClientConnection> connectionSet =
+         userMap.get(oldEntry.getDN());
+    if (connectionSet != null)
+    {
+      for (ClientConnection conn : connectionSet)
+      {
+        conn.updateAuthenticationInfo(oldEntry, newEntry);
+      }
+    }
+  }
+
+
+
+  /**
+   * 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)
+  {
+    // 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.
+    CopyOnWriteArraySet<ClientConnection> connectionSet =
+         userMap.remove(oldEntry.getDN());
+    if (connectionSet != null)
+    {
+      synchronized (this)
+      {
+        CopyOnWriteArraySet<ClientConnection> existingNewSet =
+             userMap.get(newEntry.getDN());
+        if (existingNewSet == null)
+        {
+          userMap.put(newEntry.getDN(), connectionSet);
+        }
+        else
+        {
+          existingNewSet.addAll(connectionSet);
+        }
+      }
+
+      for (ClientConnection conn : connectionSet)
+      {
+        conn.updateAuthenticationInfo(oldEntry, newEntry);
+      }
+    }
+  }
+}
+
diff --git a/opends/src/server/org/opends/server/core/BindOperation.java b/opends/src/server/org/opends/server/core/BindOperation.java
index fa694e4..0489f66 100644
--- a/opends/src/server/org/opends/server/core/BindOperation.java
+++ b/opends/src/server/org/opends/server/core/BindOperation.java
@@ -147,6 +147,10 @@
   // The DN of the user entry that is attempting to authenticate.
   private DN userEntryDN;
 
+  // The entry of the user that successfully authenticated during processing of
+  // this bind operation.
+  private Entry authenticatedUserEntry;
+
   // The DN of the user as whom a SASL authentication was attempted (regardless
   // of whether the authentication was successful) for the purpose of updating
   // password policy state information.
@@ -244,6 +248,7 @@
     responseControls         = new ArrayList<Control>(0);
     authFailureID            = 0;
     authFailureReason        = null;
+    authenticatedUserEntry   = null;
     saslAuthUserEntry        = null;
     isFirstWarning           = false;
     isGraceLogin             = false;
@@ -304,12 +309,13 @@
       this.rawBindDN = rawBindDN;
     }
 
-    bindDN            = null;
-    userEntryDN       = null;
-    responseControls  = new ArrayList<Control>(0);
-    authFailureID     = 0;
-    authFailureReason = null;
-    saslAuthUserEntry = null;
+    bindDN                 = null;
+    userEntryDN            = null;
+    responseControls       = new ArrayList<Control>(0);
+    authFailureID          = 0;
+    authFailureReason      = null;
+    authenticatedUserEntry = null;
+    saslAuthUserEntry      = null;
   }
 
 
@@ -369,6 +375,7 @@
     responseControls         = new ArrayList<Control>(0);
     authFailureID            = 0;
     authFailureReason        = null;
+    authenticatedUserEntry   = null;
     saslAuthUserEntry        = null;
     isFirstWarning           = false;
     isGraceLogin             = false;
@@ -429,11 +436,12 @@
       rawBindDN = new ASN1OctetString(bindDN.toString());
     }
 
-    responseControls  = new ArrayList<Control>(0);
-    authFailureID     = 0;
-    authFailureReason = null;
-    saslAuthUserEntry = null;
-    userEntryDN       = null;
+    responseControls       = new ArrayList<Control>(0);
+    authFailureID          = 0;
+    authFailureReason      = null;
+    authenticatedUserEntry = null;
+    saslAuthUserEntry      = null;
+    userEntryDN            = null;
   }
 
 
@@ -1481,8 +1489,8 @@
               setResultCode(ResultCode.SUCCESS);
 
               boolean isRoot = DirectoryServer.isRootDN(userEntry.getDN());
-              authInfo = new AuthenticationInfo(userEntry.getDN(),
-                                                simplePassword, isRoot);
+              authInfo = new AuthenticationInfo(userEntry, simplePassword,
+                                                isRoot);
 
 
               // See if the user's entry contains a custom size limit.
@@ -2220,6 +2228,7 @@
     // Update the authentication information for the user.
     if ((getResultCode() == ResultCode.SUCCESS) && (authInfo != null))
     {
+      authenticatedUserEntry = authInfo.getAuthenticationEntry();
       clientConnection.setAuthenticationInfo(authInfo);
       clientConnection.setSizeLimit(sizeLimit);
       clientConnection.setTimeLimit(timeLimit);
diff --git a/opends/src/server/org/opends/server/core/CompareOperation.java b/opends/src/server/org/opends/server/core/CompareOperation.java
index e0015cb..a811667 100644
--- a/opends/src/server/org/opends/server/core/CompareOperation.java
+++ b/opends/src/server/org/opends/server/core/CompareOperation.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -837,10 +837,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -855,7 +855,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
             else if (oid.equals(OID_PROXIED_AUTH_V2))
             {
@@ -882,10 +882,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -900,7 +900,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
 
             // NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/core/DeleteOperation.java b/opends/src/server/org/opends/server/core/DeleteOperation.java
index f144e2c..94748e9 100644
--- a/opends/src/server/org/opends/server/core/DeleteOperation.java
+++ b/opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -816,10 +816,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -834,7 +834,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
             else if (oid.equals(OID_PROXIED_AUTH_V2))
             {
@@ -861,10 +861,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -879,7 +879,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
 
             // NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index a971890..cf95ab6 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -224,6 +224,9 @@
   // The attribute type used to reference the "objectclass" attribute.
   private AttributeType objectClassAttributeType;
 
+  // The authenticated users manager for the server.
+  private AuthenticatedUsers authenticatedUsers;
+
   // The configuration manager that will handle the server backends.
   private BackendConfigManager backendConfigManager;
 
@@ -649,6 +652,7 @@
          new ConcurrentHashMap<String,ExtendedOperationHandler>();
     directoryServer.saslMechanismHandlers =
          new ConcurrentHashMap<String,SASLMechanismHandler>();
+    directoryServer.authenticatedUsers = new AuthenticatedUsers();
   }
 
 
@@ -1761,6 +1765,20 @@
 
 
   /**
+   * Retrieves the authenticated users manager for the Directory Server.
+   *
+   * @return  The authenticated users manager for the Directory Server.
+   */
+  public static AuthenticatedUsers getAuthenticatedUsers()
+  {
+    assert debugEnter(CLASS_NAME, "getAuthenticatedUsers");
+
+    return directoryServer.authenticatedUsers;
+  }
+
+
+
+  /**
    * Initializes the crypto manager for the Directory Server.
    *
    * @throws  ConfigException  If a configuration problem is identified while
diff --git a/opends/src/server/org/opends/server/core/ModifyDNOperation.java b/opends/src/server/org/opends/server/core/ModifyDNOperation.java
index 8ec6126..46a6402 100644
--- a/opends/src/server/org/opends/server/core/ModifyDNOperation.java
+++ b/opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -1307,10 +1307,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1325,7 +1325,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
             else if (oid.equals(OID_PROXIED_AUTH_V2))
             {
@@ -1352,10 +1352,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1370,7 +1370,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
 
             // NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/core/ModifyOperation.java b/opends/src/server/org/opends/server/core/ModifyOperation.java
index 333a9c4..999c39c 100644
--- a/opends/src/server/org/opends/server/core/ModifyOperation.java
+++ b/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -1109,10 +1109,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1127,7 +1127,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
             else if (oid.equals(OID_PROXIED_AUTH_V2))
             {
@@ -1154,10 +1154,10 @@
               }
 
 
-              DN authzDN;
+              Entry authorizationEntry;
               try
               {
-                authzDN = proxyControl.getValidatedAuthorizationDN();
+                authorizationEntry = proxyControl.getAuthorizationEntry();
               }
               catch (DirectoryException de)
               {
@@ -1172,7 +1172,7 @@
 
               // FIXME -- Should we specifically check permissions here, or let
               //          the earlier access control checks handle it?
-              setAuthorizationDN(authzDN);
+              setAuthorizationEntry(authorizationEntry);
             }
 
             // NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/core/Operation.java b/opends/src/server/org/opends/server/core/Operation.java
index fd81051..5943668 100644
--- a/opends/src/server/org/opends/server/core/Operation.java
+++ b/opends/src/server/org/opends/server/core/Operation.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -34,13 +34,13 @@
 import java.util.Map;
 
 import org.opends.server.api.ClientConnection;
-import org.opends.server.types.AuthenticationInfo;
 import org.opends.server.types.CancelRequest;
 import org.opends.server.types.CancelResult;
 import org.opends.server.types.Control;
 import org.opends.server.types.DisconnectReason;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
 import org.opends.server.types.OperationType;
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.operation.PostOperationOperation;
@@ -111,12 +111,12 @@
   // The cancel result for this operation.
   private CancelResult cancelResult;
 
-  // The authorization DN for this operation.
-  private DN authorizationDN;
-
   // The matched DN for this operation.
   private DN matchedDN;
 
+  // The entry for the authorization identify for this operation.
+  private Entry authorizationEntry;
+
   // A set of attachments associated with this operation that might be used by
   // various components during its processing.
   private Map<String,Object> attachments;
@@ -180,8 +180,8 @@
     cancelResult               = null;
     isInternalOperation        = false;
     isSynchronizationOperation = false;
-    authorizationDN =
-         clientConnection.getAuthenticationInfo().getAuthorizationDN();
+    authorizationEntry         =
+         clientConnection.getAuthenticationInfo().getAuthorizationEntry();
   }
 
 
@@ -732,6 +732,48 @@
 
 
   /**
+   * Retrieves the entry for the user that should be considered the
+   * authorization identity for this operation.  In many cases, it will be the
+   * same as the authorization entry for the underlying client connection, or
+   * {@code null} if no authentication has been performed on that connection.
+   * However, it may be some other value if special processing has been
+   * requested (e.g., the operation included a proxied authorization control).
+   * This method should not be called by pre-parse plugins because the correct
+   * value may not yet have been determined.
+   *
+   * @return  The entry for the user that should be considered the authorization
+   *          identity for this operation, or {@code null} if the authorization
+   *          identity should be the unauthenticated  user.
+   */
+  public final Entry getAuthorizationEntry()
+  {
+    assert debugEnter(CLASS_NAME, "getAuthorizationEntry");
+
+    return authorizationEntry;
+  }
+
+
+
+  /**
+   * Provides the entry for the user that should be considered the authorization
+   * identity for this operation.  This must not be called from within a plugin.
+   *
+   * @param  authorizationEntry  The entry for the user that should be
+   *                             considered the authorization identity for this
+   *                             operation, or {@code null} if it should be the
+   *                             unauthenticated user.
+   */
+  public final void setAuthorizationEntry(Entry authorizationEntry)
+  {
+    assert debugEnter(CLASS_NAME, "setAuthorizationEntry",
+                      String.valueOf(authorizationEntry));
+
+    this.authorizationEntry = authorizationEntry;
+  }
+
+
+
+  /**
    * Retrieves the authorization DN for this operation.  In many cases, it will
    * be the same as the DN of the authenticated user for the underlying
    * connection, or the null DN if no authentication has been performed on that
@@ -740,51 +782,26 @@
    * control).  This method should not be called by pre-parse plugins because
    * the correct value may not have yet been determined.
    *
-   * @return  The authorization DN for this operation.
+   * @return  The authorization DN for this operation, or the null DN if it
+   *          should be the unauthenticated user..
    */
   public final DN getAuthorizationDN()
   {
     assert debugEnter(CLASS_NAME, "getAuthorizationDN");
 
-    if (authorizationDN == null)
+    if (authorizationEntry == null)
     {
-      AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
-      if (authInfo == null)
-      {
-        return DN.nullDN();
-      }
-      else
-      {
-        return authInfo.getAuthorizationDN();
-      }
+      return DN.nullDN();
     }
     else
     {
-      return authorizationDN;
+      return authorizationEntry.getDN();
     }
   }
 
 
 
   /**
-   * Specifies the authorization DN for this operation.  This method may not be
-   * called from within a plugin.
-   *
-   * @param  authorizationDN  The authorization DN for this operation, or
-   *                          <CODE>null</CODE> if it should use the DN of the
-   *                          authenticated user.
-   */
-  public final void setAuthorizationDN(DN authorizationDN)
-  {
-    assert debugEnter(CLASS_NAME, "setAuthorizationDN",
-                      String.valueOf(authorizationDN));
-
-    this.authorizationDN = authorizationDN;
-  }
-
-
-
-  /**
    * Retrieves the set of attachments defined for this operation, as a mapping
    * between the attachment name and the associated object.
    *
diff --git a/opends/src/server/org/opends/server/core/SearchOperation.java b/opends/src/server/org/opends/server/core/SearchOperation.java
index 5667eae..d8b6f44 100644
--- a/opends/src/server/org/opends/server/core/SearchOperation.java
+++ b/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -1783,25 +1783,25 @@
             }
 
 
-            DN authzDN;
-            try
-            {
-              authzDN = proxyControl.getValidatedAuthorizationDN();
-            }
-            catch (DirectoryException de)
-            {
-              assert debugException(CLASS_NAME, "run", de);
+              Entry authorizationEntry;
+              try
+              {
+                authorizationEntry = proxyControl.getAuthorizationEntry();
+              }
+              catch (DirectoryException de)
+              {
+                assert debugException(CLASS_NAME, "run", de);
 
-              setResultCode(de.getResultCode());
-              appendErrorMessage(de.getErrorMessage());
+                setResultCode(de.getResultCode());
+                appendErrorMessage(de.getErrorMessage());
 
-              break searchProcessing;
-            }
+                break searchProcessing;
+              }
 
 
-            // FIXME -- Should we specifically check permissions here, or let
-            //          the earlier access control checks handle it?
-            setAuthorizationDN(authzDN);
+              // FIXME -- Should we specifically check permissions here, or let
+              //          the earlier access control checks handle it?
+              setAuthorizationEntry(authorizationEntry);
           }
           else if (oid.equals(OID_PROXIED_AUTH_V2))
           {
@@ -1828,25 +1828,25 @@
             }
 
 
-            DN authzDN;
-            try
-            {
-              authzDN = proxyControl.getValidatedAuthorizationDN();
-            }
-            catch (DirectoryException de)
-            {
-              assert debugException(CLASS_NAME, "run", de);
+              Entry authorizationEntry;
+              try
+              {
+                authorizationEntry = proxyControl.getAuthorizationEntry();
+              }
+              catch (DirectoryException de)
+              {
+                assert debugException(CLASS_NAME, "run", de);
 
-              setResultCode(de.getResultCode());
-              appendErrorMessage(de.getErrorMessage());
+                setResultCode(de.getResultCode());
+                appendErrorMessage(de.getErrorMessage());
 
-              break searchProcessing;
-            }
+                break searchProcessing;
+              }
 
 
-            // FIXME -- Should we specifically check permissions here, or let
-            //          the earlier access control checks handle it?
-            setAuthorizationDN(authzDN);
+              // FIXME -- Should we specifically check permissions here, or let
+              //          the earlier access control checks handle it?
+              setAuthorizationEntry(authorizationEntry);
           }
           else if (oid.equals(OID_PERSISTENT_SEARCH))
           {
diff --git a/opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java
index ef5d0f1..1978be9 100644
--- a/opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java
@@ -547,7 +547,7 @@
     bindOperation.setResultCode(ResultCode.SUCCESS);
 
     AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry.getDN(), SASL_MECHANISM_CRAM_MD5,
+         new AuthenticationInfo(userEntry, SASL_MECHANISM_CRAM_MD5,
                                 DirectoryServer.isRootDN(userEntry.getDN()));
     bindOperation.setAuthenticationInfo(authInfo);
     return;
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index 754879d..7036ebe 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -1011,7 +1011,7 @@
 
 
     AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry.getDN(), SASL_MECHANISM_DIGEST_MD5,
+         new AuthenticationInfo(userEntry, SASL_MECHANISM_DIGEST_MD5,
                                 DirectoryServer.isRootDN(userEntry.getDN()));
     bindOperation.setAuthenticationInfo(authInfo);
     return;
diff --git a/opends/src/server/org/opends/server/extensions/ExternalSASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/ExternalSASLMechanismHandler.java
index 2a2d491..dd71ace 100644
--- a/opends/src/server/org/opends/server/extensions/ExternalSASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ExternalSASLMechanismHandler.java
@@ -455,7 +455,7 @@
 
 
     AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry.getDN(), SASL_MECHANISM_EXTERNAL,
+         new AuthenticationInfo(userEntry, SASL_MECHANISM_EXTERNAL,
                                 DirectoryServer.isRootDN(userEntry.getDN()));
     bindOperation.setAuthenticationInfo(authInfo);
     bindOperation.setResultCode(ResultCode.SUCCESS);
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
index e44e3ca..a9954f5 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -419,10 +419,10 @@
     {
       // The authentication was successful, so set the proper state information
       // in the client connection and return success.
-      DN userDN = stateInfo.getUserEntry().getDN();
+      Entry userEntry = stateInfo.getUserEntry();
       AuthenticationInfo authInfo =
-           new AuthenticationInfo(userDN, SASL_MECHANISM_GSSAPI,
-                                  DirectoryServer.isRootDN(userDN));
+           new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
+                                  DirectoryServer.isRootDN(userEntry.getDN()));
       bindOperation.setAuthenticationInfo(authInfo);
       bindOperation.setResultCode(ResultCode.SUCCESS);
 
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java b/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
index 6d0f3b1..35cc403 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
+++ b/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
@@ -430,7 +430,7 @@
     // The authentication was successful, so set the proper state information
     // in the client connection and return success.
     AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry.getDN(), SASL_MECHANISM_GSSAPI,
+         new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
                                 DirectoryServer.isRootDN(userEntry.getDN()));
     bindOperation.setAuthenticationInfo(authInfo);
     bindOperation.setResultCode(ResultCode.SUCCESS);
diff --git a/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java b/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
index d3f715f..5eafa77 100644
--- a/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
+++ b/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -310,8 +310,8 @@
     }
 
 
-    // Get the DN of the user that issued the request.
-    DN requestorDN = operation.getAuthorizationDN();
+    // 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
@@ -329,8 +329,7 @@
         // authenticated.
         ClientConnection   clientConnection = operation.getClientConnection();
         AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
-        if ((! authInfo.isAuthenticated()) || (requestorDN == null) ||
-            (requestorDN.isNullDN()))
+        if ((! authInfo.isAuthenticated()) || (requestorEntry == null))
         {
           operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
 
@@ -342,7 +341,7 @@
 
 
         // Retrieve a write lock on that user's entry.
-        userDN = requestorDN;
+        userDN = requestorEntry.getDN();
 
         for (int i=0; i < 3; i++)
         {
@@ -366,11 +365,7 @@
         }
 
 
-        userEntry = getEntryByDN(operation, userDN);
-        if (userEntry == null)
-        {
-          return;
-        }
+        userEntry = requestorEntry;
       }
       else
       {
@@ -500,8 +495,9 @@
 
       // Determine whether the user is changing his own password or if it's an
       // administrative reset.
-      boolean selfChange = ((userIdentity == null) || (requestorDN == null) ||
-                            userDN.equals(requestorDN));
+      boolean selfChange = ((userIdentity == null) ||
+                            (requestorEntry == null) ||
+                            userDN.equals(requestorEntry.getDN()));
 
 
       // See if the account is locked.  If so, then reject the request.
@@ -1085,9 +1081,14 @@
       }
       else
       {
+        if (selfChange && (requestorEntry == null))
+        {
+          requestorEntry = userEntry;
+        }
+
         // Get an internal connection and use it to perform the modification.
-        boolean isRoot = DirectoryServer.isRootDN(requestorDN);
-        AuthenticationInfo authInfo = new AuthenticationInfo(requestorDN,
+        boolean isRoot = DirectoryServer.isRootDN(requestorEntry.getDN());
+        AuthenticationInfo authInfo = new AuthenticationInfo(requestorEntry,
                                                              isRoot);
         InternalClientConnection internalConnection = new
              InternalClientConnection(authInfo);
diff --git a/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
index 5d56eb5..3adb6c0 100644
--- a/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
@@ -435,7 +435,7 @@
     bindOperation.setResultCode(ResultCode.SUCCESS);
 
     AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry.getDN(), SASL_MECHANISM_PLAIN,
+         new AuthenticationInfo(userEntry, SASL_MECHANISM_PLAIN,
                                 DirectoryServer.isRootDN(userEntry.getDN()));
     bindOperation.setAuthenticationInfo(authInfo);
     return;
diff --git a/opends/src/server/org/opends/server/messages/CoreMessages.java b/opends/src/server/org/opends/server/messages/CoreMessages.java
index 67f2b53..295310f 100644
--- a/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6150,6 +6150,17 @@
 
 
   /**
+   * The message ID for the message that will be used if a client connection is
+   * terminated because the associated authentication or authorization entry was
+   * removed from the server.  It takes a single argument, which is the DN of
+   * the entry that has been removed.
+   */
+  public static final int MSGID_CLIENTCONNECTION_DISCONNECT_DUE_TO_DELETE =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_WARNING | 588;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined
    * in this class.
    */
@@ -8321,6 +8332,11 @@
                      "Rejecting the requested operation  " +
                      "because the connection has not been authenticated.");
 
+
+    registerMessage(MSGID_CLIENTCONNECTION_DISCONNECT_DUE_TO_DELETE,
+                    "Terminating the client connection because its " +
+                    "associated authentication or authorization entry %s has " +
+                    "been deleted.");
   }
 }
 
diff --git a/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java b/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
index 8508a8e..a56704f 100644
--- a/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -32,6 +32,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -46,6 +47,7 @@
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.CompareOperation;
 import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ExtendedOperation;
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.ModifyDNOperation;
@@ -70,6 +72,7 @@
 import org.opends.server.types.ErrorLogSeverity;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
 import org.opends.server.types.IntermediateResponse;
 import org.opends.server.types.Modification;
 import org.opends.server.types.ObjectClass;
@@ -79,10 +82,12 @@
 import org.opends.server.types.SearchResultReference;
 import org.opends.server.types.SearchScope;
 
+import static org.opends.server.config.ConfigConstants.*;
 import static org.opends.server.loggers.Debug.*;
 import static org.opends.server.loggers.Error.*;
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.messages.ProtocolMessages.*;
+import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
 
@@ -152,13 +157,60 @@
 
     // This connection will be authenticated as a root user so that no
     // access control will be enforced.
-    String dnString = "cn=Internal Client";
+    String commonName    = "Internal Client";
+    String shortDNString = "cn=" + commonName;
+    String fullDNString  = shortDNString + ",cn=Root DNs,cn=config";
     try
     {
-      DN internalUserDN = DN.decode(dnString);
+      LinkedHashMap<ObjectClass,String> objectClasses =
+           new LinkedHashMap<ObjectClass,String>();
+      ObjectClass topOC = DirectoryServer.getTopObjectClass();
+      ObjectClass personOC = DirectoryServer.getObjectClass(OC_PERSON,
+                                                            true);
+      ObjectClass rootOC = DirectoryServer.getObjectClass(OC_ROOT_DN,
+                                                          true);
+
+      objectClasses.put(topOC, topOC.getPrimaryName());
+      objectClasses.put(personOC, personOC.getPrimaryName());
+      objectClasses.put(rootOC, rootOC.getPrimaryName());
+
+
+      LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
+           new LinkedHashMap<AttributeType,List<Attribute>>();
+      AttributeType cnAT =
+           DirectoryServer.getAttributeType(ATTR_COMMON_NAME, true);
+      AttributeType snAT = DirectoryServer.getAttributeType(ATTR_SN,
+                                                            true);
+      AttributeType altDNAT =
+           DirectoryServer.getAttributeType(
+                ATTR_ROOTDN_ALTERNATE_BIND_DN, true);
+
+      LinkedList<Attribute> attrList = new LinkedList<Attribute>();
+      attrList.add(new Attribute(ATTR_COMMON_NAME, commonName));
+      userAttrs.put(cnAT, attrList);
+
+      attrList = new LinkedList<Attribute>();
+      attrList.add(new Attribute(ATTR_SN, commonName));
+      userAttrs.put(snAT, attrList);
+
+      attrList = new LinkedList<Attribute>();
+      attrList.add(new Attribute(ATTR_ROOTDN_ALTERNATE_BIND_DN,
+                                 shortDNString));
+      userAttrs.put(altDNAT, attrList);
+
+
+      LinkedHashMap<AttributeType,List<Attribute>> operationalAttrs =
+           new LinkedHashMap<AttributeType,List<Attribute>>();
+      // FIXME -- Add privileges here.
+
+
+      DN internalUserDN = DN.decode(fullDNString);
+      Entry internalUserEntry =
+                 new Entry(internalUserDN, objectClasses, userAttrs,
+                           operationalAttrs);
 
       this.authenticationInfo =
-           new AuthenticationInfo(internalUserDN, true);
+           new AuthenticationInfo(internalUserEntry, true);
     }
     catch (DirectoryException de)
     {
@@ -166,7 +218,7 @@
 
       logError(ErrorLogCategory.CONNECTION_HANDLING,
                ErrorLogSeverity.SEVERE_ERROR,
-               MSGID_INTERNAL_CANNOT_DECODE_DN, dnString,
+               MSGID_INTERNAL_CANNOT_DECODE_DN, fullDNString,
                stackTraceToSingleLineString(de));
     }
 
@@ -1535,7 +1587,10 @@
                       String.valueOf(messageID));
 
     // No implementation is required since there is nothing to
-    // disconnect.
+    // disconnect.  Further, since there is no real disconnect, we can
+    // wait to have the garbage collector call
+    // finalizeConnectionInternal whenever this internal connection is
+    // garbage collected.
   }
 
 
@@ -1735,7 +1790,12 @@
     buffer.append("InternalClientConnection(connID=");
     buffer.append(connectionID);
     buffer.append(", authDN=\"");
-    buffer.append(getAuthenticationInfo().getAuthenticationDN());
+
+    if (getAuthenticationInfo() != null)
+    {
+      buffer.append(getAuthenticationInfo().getAuthenticationDN());
+    }
+
     buffer.append("\")");
   }
 }
diff --git a/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java b/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
index 79678d2..985f539 100644
--- a/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.protocols.jmx;
 
@@ -895,6 +895,7 @@
       return;
     }
     disconnectStarted = true ;
+    finalizeConnectionInternal();
 
 
 
@@ -1144,6 +1145,7 @@
    */
   protected void finalize()
   {
+    super.finalize();
     disconnect(DisconnectReason.OTHER, false, null, -1);
   }
 }
diff --git a/opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java b/opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java
index 16d02cf..0013295 100644
--- a/opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java
+++ b/opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.protocols.jmx;
 
@@ -265,7 +265,7 @@
       bindPW = new ASN1OctetString(password);
     }
 
-    AuthenticationInfo authInfo = new AuthenticationInfo(bindDN, bindPW, false);
+    AuthenticationInfo authInfo = new AuthenticationInfo();
     JmxClientConnection jmxClientConnection = new JmxClientConnection(
         jmxConnectionHandler, authInfo);
 
@@ -284,6 +284,8 @@
           "bind",
           "User is authenticated");
 
+      authInfo = bindOp.getAuthenticationInfo();
+      jmxClientConnection.setAuthenticationInfo(authInfo);
       return jmxClientConnection;
     }
     else
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 147535a..9059c8d 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.protocols.ldap;
 
@@ -1032,6 +1032,7 @@
     }
 
     cancelAllOperations(new CancelRequest(true, message));
+    finalizeConnectionInternal();
 
 
     // See if we should send a notification to the client.  If so, then
diff --git a/opends/src/server/org/opends/server/types/AuthenticationInfo.java b/opends/src/server/org/opends/server/types/AuthenticationInfo.java
index 05d8df8..a570693 100644
--- a/opends/src/server/org/opends/server/types/AuthenticationInfo.java
+++ b/opends/src/server/org/opends/server/types/AuthenticationInfo.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.types;
 
@@ -34,6 +34,7 @@
 import java.util.Set;
 
 import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.util.Validator.*;
 
 
 
@@ -68,11 +69,12 @@
   // other operation will be allowed.
   private boolean mustChangePassword;
 
-  // The DN of the user that is currently authenticated.
-  private DN authenticationDN;
+  // The entry of the user that is currently authenticated.
+  private Entry authenticationEntry;
 
-  // The authorization DN for this authentication info structure.
-  private DN authorizationDN;
+  // The entry of the user that will be used as the default
+  // authorization identity.
+  private Entry authorizationEntry;
 
   // The type of authentication performed on this connection.
   private Set<AuthenticationType> authenticationTypes;
@@ -95,8 +97,8 @@
     mustChangePassword  = false;
     simplePassword      = null;
     authenticationTypes = new HashSet<AuthenticationType>(0);
-    authenticationDN    = null;
-    authorizationDN     = null;
+    authenticationEntry = null;
+    authorizationEntry  = null;
     saslMechanisms      = new HashSet<String>(0);
   }
 
@@ -106,24 +108,25 @@
    * Creates a new set of authentication information to be used for
    * clients that are authenticated internally.
    *
-   * @param  authenticationDN  The DN of the user that has
-   *                           authenticated.
-   * @param  isRoot            Indicates whether the authenticated
-   *                           user is a root user.
+   * @param  authenticationEntry  The entry of the user that has
+   *                              authenticated, or {@code null} to
+   *                              indicate an unauthenticated user.
+   * @param  isRoot               Indicates whether the authenticated
+   *                              user is a root user.
    */
-  public AuthenticationInfo(DN authenticationDN, boolean isRoot)
+  public AuthenticationInfo(Entry authenticationEntry, boolean isRoot)
   {
     assert debugConstructor(CLASS_NAME,
-                            String.valueOf(authenticationDN),
+                            String.valueOf(authenticationEntry),
                             String.valueOf(isRoot));
 
-    this.authenticationDN = authenticationDN;
-    this.isRoot           = isRoot;
+    this.authenticationEntry = authenticationEntry;
+    this.isRoot              = isRoot;
 
-    isAuthenticated     = true;
+    isAuthenticated     = (authenticationEntry != null);
     mustChangePassword  = false;
     simplePassword      = null;
-    authorizationDN     = authenticationDN;
+    authorizationEntry  = authenticationEntry;
     saslMechanisms      = new HashSet<String>(0);
     authenticationTypes = new HashSet<AuthenticationType>(1);
 
@@ -136,28 +139,32 @@
    * Creates a new set of authentication information to be used for
    * clients that have successfully performed simple authentication.
    *
-   * @param  authenticationDN  The DN of the user that has
-   *                           authenticated.
-   * @param  simplePassword    The password that was used to perform
-   *                           the simple authentication.
-   * @param  isRoot            Indicates whether the authenticated
-   *                           user is a root user.
+   * @param  authenticationEntry  The entry of the user that has
+   *                              authenticated.  It must not be
+   *                              {@code null}.
+   * @param  simplePassword       The password that was used to
+   *                              perform the simple authentication.
+   *                              It must not be {@code null}.
+   * @param  isRoot               Indicates whether the authenticated
+   *                              user is a root user.
    */
-  public AuthenticationInfo(DN authenticationDN,
+  public AuthenticationInfo(Entry authenticationEntry,
                             ByteString simplePassword, boolean isRoot)
   {
     assert debugConstructor(CLASS_NAME,
-                            String.valueOf(authenticationDN),
+                            String.valueOf(authenticationEntry),
                             String.valueOf(simplePassword),
                             String.valueOf(isRoot));
 
-    this.authenticationDN = authenticationDN;
-    this.simplePassword   = simplePassword;
-    this.isRoot           = isRoot;
+    ensureNotNull(authenticationEntry, simplePassword);
+
+    this.authenticationEntry = authenticationEntry;
+    this.simplePassword      = simplePassword;
+    this.isRoot              = isRoot;
 
     isAuthenticated     = true;
     mustChangePassword  = false;
-    authorizationDN     = authenticationDN;
+    authorizationEntry  = authenticationEntry;
     saslMechanisms      = new HashSet<String>(0);
     authenticationTypes = new HashSet<AuthenticationType>(1);
 
@@ -170,28 +177,32 @@
    * Creates a new set of authentication information to be used for
    * clients that have authenticated using a SASL mechanism.
    *
-   * @param  authenticationDN  The DN of the user that has
-   *                           authenticated.
-   * @param  saslMechanism     The SASL mechanism used to
-   *                           authenticate.  Note that this must be
-   *                           provided in all-uppercase characters.
-   * @param  isRoot            Indicates whether the authenticated
-   *                           user is a root user.
+   * @param  authenticationEntry  The entry of the user that has
+   *                              authenticated.  It must not be
+   *                              {@code null}.
+   * @param  saslMechanism        The SASL mechanism used to
+   *                              authenticate.  This must be provided
+   *                              in all-uppercase characters and must
+   *                              not be {@code null}.
+   * @param  isRoot               Indicates whether the authenticated
+   *                              user is a root user.
    */
-  public AuthenticationInfo(DN authenticationDN, String saslMechanism,
-                            boolean isRoot)
+  public AuthenticationInfo(Entry authenticationEntry,
+                            String saslMechanism, boolean isRoot)
   {
     assert debugConstructor(CLASS_NAME,
-                            String.valueOf(authenticationDN),
+                            String.valueOf(authenticationEntry),
                             String.valueOf(saslMechanism),
                             String.valueOf(isRoot));
 
-    this.authenticationDN = authenticationDN;
-    this.isRoot           = isRoot;
+    ensureNotNull(authenticationEntry, saslMechanism);
+
+    this.authenticationEntry = authenticationEntry;
+    this.isRoot              = isRoot;
 
     isAuthenticated    = true;
     mustChangePassword = false;
-    authorizationDN    = authenticationDN;
+    authorizationEntry = authenticationEntry;
     simplePassword     = null;
 
     authenticationTypes = new HashSet<AuthenticationType>(1);
@@ -207,35 +218,43 @@
    * Creates a new set of authentication information to be used for
    * clients that have authenticated using a SASL mechanism.
    *
-   * @param  authenticationDN  The DN of the user that has
-   *                           authenticated.
-   * @param  authorizationDN   The authorization DN for the
-   *                           authenticated user.
-   * @param  saslMechanism     The SASL mechanism used to
-   *                           authenticate.  Note that this must be
-   *                           provided in all-uppercase characters.
-   * @param  isRoot            Indicates whether the authenticated
-   *                           user is a root user.
+   * @param  authenticationEntry  The entry of the user that has
+   *                              authenticated.  It must not be
+   *                              {@code null}.
+   * @param  authorizationEntry   The entry of the user that will be
+   *                              used as the default authorization
+   *                              identity, or {@code null} to
+   *                              indicate that it should be the same
+   *                              as the authentication entry.
+   * @param  saslMechanism        The SASL mechanism used to
+   *                              authenticate.  This must be provided
+   *                              in all-uppercase characters and must
+   *                              not be {@code null}.
+   * @param  isRoot               Indicates whether the authenticated
+   *                              user is a root user.
    */
-  public AuthenticationInfo(DN authenticationDN, DN authorizationDN,
+  public AuthenticationInfo(Entry authenticationEntry,
+                            Entry authorizationEntry,
                             String saslMechanism, boolean isRoot)
   {
     assert debugConstructor(CLASS_NAME,
-                            String.valueOf(authenticationDN),
-                            String.valueOf(authorizationDN),
+                            String.valueOf(authenticationEntry),
+                            String.valueOf(authorizationEntry),
                             String.valueOf(saslMechanism),
                             String.valueOf(isRoot));
 
-    this.authenticationDN = authenticationDN;
-    this.isRoot           = isRoot;
+    ensureNotNull(authenticationEntry, saslMechanism);
 
-    if (authorizationDN == null)
+    this.authenticationEntry = authenticationEntry;
+    this.isRoot              = isRoot;
+
+    if (authorizationEntry == null)
     {
-      this.authorizationDN = authenticationDN;
+      this.authorizationEntry = authenticationEntry;
     }
     else
     {
-      this.authorizationDN = authorizationDN;
+      this.authorizationEntry = authorizationEntry;
     }
 
     isAuthenticated    = true;
@@ -255,9 +274,8 @@
    * Indicates whether this client has successfully authenticated to
    * the server.
    *
-   * @return  <CODE>true</CODE> if this client has successfully
-   *          authenticated to the server, or <CODE>false</CODE> if
-   *          not.
+   * @return  {@code true} if this client has successfully
+   *          authenticated to the server, or {@code false} if not.
    */
   public boolean isAuthenticated()
   {
@@ -280,8 +298,8 @@
     isRoot              = false;
     mustChangePassword  = false;
     simplePassword      = null;
-    authenticationDN    = null;
-    authorizationDN     = null;
+    authenticationEntry = null;
+    authorizationEntry  = null;
 
     authenticationTypes.clear();
     saslMechanisms.clear();
@@ -292,8 +310,8 @@
   /**
    * Indicates whether this client should be considered a root user.
    *
-   * @return  <CODE>true</CODE> if this client should be considered a
-   *          root user, or <CODE>false</CODE> if not.
+   * @return  {@code true} if this client should be considered a root
+   *          user, or {@code false} if not.
    */
   public boolean isRoot()
   {
@@ -305,12 +323,12 @@
 
 
   /**
-   * Indicates whether the associated user must change their password
-   * before any other operation will be allowed.
+   * Indicates whether the authenticated user must change his/her
+   * password before any other operation will be allowed.
    *
-   * @return  <CODE>true</CODE> if the user must change their password
+   * @return  {@code true} if the user must change his/her password
    *          before any other operation will be allowed, or
-   *          <CODE>false</CODE> if not.
+   *          {@code false} if not.
    */
   public boolean mustChangePassword()
   {
@@ -322,12 +340,13 @@
 
 
   /**
-   * Specifies whether the associated user must change their password
-   * before any other operation will be allowed.
+   * Specifies whether the authenticated user must change his/her
+   * password before any other operation will be allowed.
    *
-   * @param  mustChangePassword  Specifies whether the associated user
-   *                             must change their password before any
-   *                             other operation will be allowed.
+   * @param  mustChangePassword  Specifies whether the authenticated
+   *                             user must change his/her password
+   *                             before any other operation will be
+   *                             allowed.
    */
   public void setMustChangePassword(boolean mustChangePassword)
   {
@@ -346,9 +365,8 @@
    * @param  authenticationType  The authentication type for which to
    *                             make the determination.
    *
-   * @return  <CODE>true</CODE> if the client has authenticated using
-   *          the specified authentication type, or <CODE>false</CODE>
-   *          if not.
+   * @return  {@code true} if the client has authenticated using the
+   *          specified authentication type, or {@code false} if not.
    */
   public boolean hasAuthenticationType(AuthenticationType
                                             authenticationType)
@@ -368,9 +386,9 @@
    * @param  types  The collection of authentication types for which
    *                to make the determination.
    *
-   * @return  <CODE>true</CODE> if the client has authenticated using
-   *          any of the specified authentication types, or
-   *          <CODE>false</CODE> if not.
+   * @return  {@code true} if the client has authenticated using any
+   *          of the specified authentication types, or {@code false}
+   *          if not.
    */
   public boolean hasAnyAuthenticationType(
                       Collection<AuthenticationType> types)
@@ -426,30 +444,83 @@
 
 
   /**
-   * Retrieves the DN of the user as whom the client is authenticated.
+   * Retrieves the entry for the user as whom the client is
+   * authenticated.
    *
-   * @return  The DN of the user as whom the client is authenticated,
-   *          or <CODE>null</CODE> if the client is unauthenticated.
+   * @return  The entry for the user as whom the client is
+   *          authenticated, or {@code null} if the client is
+   *          unauthenticated.
    */
-  public DN getAuthenticationDN()
+  public Entry getAuthenticationEntry()
   {
-    assert debugEnter(CLASS_NAME, "getAuthenticationDN");
+    assert debugEnter(CLASS_NAME, "getAuthenticationEntry");
 
-    return authenticationDN;
+    return authenticationEntry;
   }
 
 
 
   /**
-   * Retrieves the authorization DN for this client.
+   * Retrieves the DN of the user as whom the client is authenticated.
    *
-   * @return  The authorization DN for this client.
+   * @return  The DN of the user as whom the client is authenticated,
+   *          or {@code null} if the client is unauthenticated.
+   */
+  public DN getAuthenticationDN()
+  {
+    assert debugEnter(CLASS_NAME, "getAuthenticationDN");
+
+    if (authenticationEntry == null)
+    {
+      return null;
+    }
+    else
+    {
+      return authenticationEntry.getDN();
+    }
+  }
+
+
+
+  /**
+   * Retrieves the entry for the user that should be used as the
+   * default authorization identity.
+   *
+   * @return  The entry for the user that should be used as the
+   *          default authorization identity, or {@code null} if the
+   *          authorization identity should be the unauthenticated
+   *          user.
+   */
+  public Entry getAuthorizationEntry()
+  {
+    assert debugEnter(CLASS_NAME, "getAuthorizationEntry");
+
+    return authorizationEntry;
+  }
+
+
+
+  /**
+   * Retrieves the DN for the user that should be used as the default
+   * authorization identity.
+   *
+   * @return  The DN for the user that should be used as the default
+   *          authorization identity, or {@code null} if the
+   *          authorization identity should be the unauthenticated
+   *          user.
    */
   public DN getAuthorizationDN()
   {
     assert debugEnter(CLASS_NAME, "getAuthorizationDN");
 
-    return authorizationDN;
+    if (authorizationEntry == null)
+    {
+      return null;
+    }
+    else
+    {
+      return authorizationEntry.getDN();
+    }
   }
 
 
@@ -459,8 +530,8 @@
    * authentication.
    *
    * @return  The password that the client used for simple
-   *          authentication, or <CODE>null</CODE> if the client is
-   *          not authenticated using simple authentication.
+   *          authentication, or {@code null} if the client is not
+   *          authenticated using simple authentication.
    */
   public ByteString getSimplePassword()
   {
@@ -479,9 +550,8 @@
    *                        determination.  Note that this must be
    *                        provided in all uppercase characters.
    *
-   * @return  <CODE>true</CODE> if the client is authenticated using
-   *          the specified SASL mechanism, or <CODE>false</CODE> if
-   *          not.
+   * @return  {@code true} if the client is authenticated using the
+   *          specified SASL mechanism, or {@code false} if not.
    */
   public boolean hasSASLMechanism(String saslMechanism)
   {
@@ -500,9 +570,9 @@
    * @param  mechanisms  The collection of SASL mechanisms for which
    *                     to make the determination.
    *
-   * @return  <CODE>true</CODE> if the client has authenticated using
-   *          any of the provided SASL mechanisms, or
-   *          <CODE>false</CODE> if not.
+   * @return  {@code true} if the client has authenticated using any
+   *          of the provided SASL mechanisms, or {@code false} if
+   *          not.
    */
   public boolean hasAnySASLMechanism(Collection<String> mechanisms)
   {
@@ -560,39 +630,6 @@
 
 
   /**
-   * Indicates whether the user associated with this authentication
-   * info is a member of the group with the specified DN.
-   *
-   * @param  groupDN  The DN of the group for which to make the
-   *                  determination.
-   *
-   * @return  <CODE>true</CODE> if the authenticated user is a member
-   *          of the specified group, or <CODE>false</CODE> if not.
-   */
-  public boolean isMemberOf(DN groupDN)
-  {
-    // NYI
-    return false;
-  }
-
-
-
-  /**
-   * Retrieves the DNs of the groups in which the user associated with
-   * this authentication info is a member.
-   *
-   * @return  The DNs of the groups in which the user associated with
-   *          this authentication info is a member.
-   */
-  public Collection<DN> getMembershipDNs()
-  {
-    // NYI
-    return null;
-  }
-
-
-
-  /**
    * Retrieves a string representation of this authentication info
    * structure.
    *
@@ -630,16 +667,22 @@
     buffer.append(",mustChangePassword=");
     buffer.append(mustChangePassword);
     buffer.append(",authenticationDN=\"");
-    buffer.append(authenticationDN);
 
-    if ((authorizationDN == null) ||
-        authorizationDN.equals(authenticationDN))
+    if (authenticationEntry != null)
     {
-      buffer.append("\",authorizationDN=\"");
-      buffer.append(authorizationDN);
+      authenticationEntry.getDN().toString(buffer);
     }
 
-    buffer.append("\"");
+    if (authorizationEntry == null)
+    {
+      buffer.append("\",authorizationDN=\"\"");
+    }
+    else
+    {
+      buffer.append("\",authorizationDN=\"");
+      authorizationEntry.getDN().toString(buffer);
+      buffer.append("\"");
+    }
 
     if (! authenticationTypes.isEmpty())
     {
@@ -694,5 +737,44 @@
 
     buffer.append(")");
   }
+
+
+
+  /**
+   * Creates a duplicate of this {@code AuthenticationInfo} object
+   * with the new authentication and authorization entries.
+   *
+   * @param  newAuthenticationEntry  The updated entry for the user
+   *                                 as whom the associated client
+   *                                 connection is authenticated.
+   * @param  newAuthorizationEntry   The updated entry for the default
+   *                                 authorization identity for the
+   *                                 associated client connection.
+   *
+   * @return  The duplicate of this {@code AuthenticationInfo} object
+   *          with the specified authentication and authorization
+   *          entries.
+   */
+  public AuthenticationInfo duplicate(Entry newAuthenticationEntry,
+                                      Entry newAuthorizationEntry)
+  {
+    assert debugEnter(CLASS_NAME, "duplicate",
+                      String.valueOf(newAuthenticationEntry),
+                      String.valueOf(newAuthorizationEntry));
+
+    AuthenticationInfo authInfo = new AuthenticationInfo();
+
+    authInfo.simplePassword      = simplePassword;
+    authInfo.isAuthenticated     = isAuthenticated;
+    authInfo.isRoot              = isRoot;
+    authInfo.mustChangePassword  = mustChangePassword;
+    authInfo.authenticationEntry = newAuthenticationEntry;
+    authInfo.authorizationEntry  = newAuthorizationEntry;
+
+    authInfo.authenticationTypes.addAll(authenticationTypes);
+    authInfo.saslMechanisms.addAll(saslMechanisms);
+
+    return authInfo;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index b915876..2f13a5a 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -338,6 +338,14 @@
 
 
   /**
+   * The name of the standard attribute that is used to hold surnames, formatted
+   * in all lowercase.
+   */
+  public static final String ATTR_SN = "sn";
+
+
+
+  /**
    * The name of the standard attribute that is used to specify the location
    * for the Directory Server schema, formatted in camel case.
    */
@@ -719,6 +727,13 @@
 
 
   /**
+   * The name of the person objectclass, formatted in all lowercase characters.
+   */
+  public static final String OC_PERSON = "person";
+
+
+
+  /**
    * The name of the standard objectclass that is used to indicate that an entry
    * is a smart referral, formatted in all lowercase.
    */
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV1ControlTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV1ControlTestCase.java
index b0b6a7c..3515430 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV1ControlTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV1ControlTestCase.java
@@ -419,7 +419,7 @@
     ProxiedAuthV1Control proxyControl =
          new ProxiedAuthV1Control(DN.nullDN());
 
-    assertTrue(proxyControl.getValidatedAuthorizationDN().isNullDN());
+    assertNull(proxyControl.getAuthorizationEntry());
   }
 
 
@@ -450,7 +450,7 @@
     ProxiedAuthV1Control proxyControl =
          new ProxiedAuthV1Control(DN.decode("uid=test,o=test"));
 
-    assertEquals(proxyControl.getValidatedAuthorizationDN(),
+    assertEquals(proxyControl.getAuthorizationEntry().getDN(),
                  DN.decode("uid=test,o=test"));
   }
 
@@ -471,7 +471,7 @@
     ProxiedAuthV1Control proxyControl =
          new ProxiedAuthV1Control(DN.decode("uid=test,o=test"));
 
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
@@ -501,7 +501,7 @@
     ProxiedAuthV1Control proxyControl =
          new ProxiedAuthV1Control(DN.decode("uid=test,o=test"));
 
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV2ControlTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV2ControlTestCase.java
index 79fea63..72b044f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV2ControlTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ProxiedAuthV2ControlTestCase.java
@@ -309,7 +309,7 @@
   {
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString(""));
-    assertEquals(proxyControl.getValidatedAuthorizationDN(), DN.nullDN());
+    assertNull(proxyControl.getAuthorizationEntry());
   }
 
 
@@ -326,7 +326,7 @@
   {
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("dn:"));
-    assertEquals(proxyControl.getValidatedAuthorizationDN(), DN.nullDN());
+    assertNull(proxyControl.getAuthorizationEntry());
   }
 
 
@@ -355,7 +355,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("dn:uid=test,o=test"));
-    assertEquals(proxyControl.getValidatedAuthorizationDN(),
+    assertEquals(proxyControl.getAuthorizationEntry().getDN(),
                  DN.decode("uid=test,o=test"));
   }
 
@@ -375,7 +375,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("dn:uid=test,o=test"));
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
@@ -406,7 +406,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("dn:uid=test,o=test"));
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
@@ -423,7 +423,7 @@
   {
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("u:"));
-    assertEquals(proxyControl.getValidatedAuthorizationDN(), DN.nullDN());
+    assertNull(proxyControl.getAuthorizationEntry());
   }
 
 
@@ -452,7 +452,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("u:test"));
-    assertEquals(proxyControl.getValidatedAuthorizationDN(),
+    assertEquals(proxyControl.getAuthorizationEntry().getDN(),
                  DN.decode("uid=test,o=test"));
   }
 
@@ -472,7 +472,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("u:test"));
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
@@ -503,7 +503,7 @@
 
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("u:test"));
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
@@ -520,7 +520,7 @@
   {
     ProxiedAuthV2Control proxyControl =
          new ProxiedAuthV2Control(new ASN1OctetString("invalid"));
-    proxyControl.getValidatedAuthorizationDN();
+    proxyControl.getAuthorizationEntry();
   }
 
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
index 6721b0c..d9149a2 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
@@ -831,7 +831,7 @@
 
     // Get a client connection authenticated as user1 and make sure it handles
     // group operations correctly.
-    authInfo = new AuthenticationInfo(user1DN, false);
+    authInfo = new AuthenticationInfo(DirectoryServer.getEntry(user1DN), false);
     InternalClientConnection conn1 = new InternalClientConnection(authInfo);
     searchOperation =
          new InternalSearchOperation(conn1, conn1.nextOperationID(),
@@ -862,7 +862,7 @@
 
     // Get a client connection authenticated as user2 and make sure it handles
     // group operations correctly.
-    authInfo = new AuthenticationInfo(user2DN, false);
+    authInfo = new AuthenticationInfo(DirectoryServer.getEntry(user2DN), false);
     InternalClientConnection conn2 = new InternalClientConnection(authInfo);
     searchOperation =
          new InternalSearchOperation(conn2, conn2.nextOperationID(),
@@ -893,7 +893,7 @@
 
     // Get a client connection authenticated as user3 and make sure it handles
     // group operations correctly.
-    authInfo = new AuthenticationInfo(user3DN, false);
+    authInfo = new AuthenticationInfo(DirectoryServer.getEntry(user3DN), false);
     InternalClientConnection conn3 = new InternalClientConnection(authInfo);
     searchOperation =
          new InternalSearchOperation(conn3, conn3.nextOperationID(),
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java
index 437b915..58c7c1f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -143,8 +143,7 @@
     assertEquals(addOp.getResultCode(), ResultCode.SUCCESS);
 
 
-    conn = new InternalClientConnection(new AuthenticationInfo(e.getDN(),
-                                                               false));
+    conn = new InternalClientConnection(new AuthenticationInfo(e, false));
     ExtendedOperation extOp =
          conn.processExtendedOperation(OID_WHO_AM_I_REQUEST, null);
     assertEquals(extOp.getResultCode(), ResultCode.SUCCESS);
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
index 9d6a9fb..c0a2e82 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
@@ -108,14 +108,31 @@
   public Object[][] getInternalConnections()
          throws Exception
   {
+    DN dmDN = DN.decode("cn=Directory Manager,cn=Root DNs,cn=config");
+    Entry dmEntry = DirectoryServer.getEntry(dmDN);
+
+    TestCaseUtils.initializeTestBackend(true);
+    Entry userEntry = 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");
+    TestCaseUtils.addEntry(userEntry);
+
     return new Object[][]
     {
       new Object[] { InternalClientConnection.getRootConnection() },
       new Object[] { new InternalClientConnection(new AuthenticationInfo()) },
       new Object[] { new InternalClientConnection(
-           new AuthenticationInfo(DN.decode("cn=Directory Manager"), true)) },
+           new AuthenticationInfo(dmEntry, true)) },
       new Object[] { new InternalClientConnection(
-           new AuthenticationInfo(DN.decode("uid=test,o=test"), false)) },
+           new AuthenticationInfo(userEntry, false)) }
     };
   }
 

--
Gitblit v1.10.0