From 4ba41d9ca10d151a6c4f80e48c78e322c98d5549 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sat, 04 Aug 2007 22:25:50 +0000
Subject: [PATCH] Update the "Who Am I?" extended operation handler so that it supports the use of the proxied authorization control (versions 1 and 2).  Also, add test cases that cover the use of the proxied auth control and an alternate authorization identity from a SASL bind.

---
 opends/src/server/org/opends/server/extensions/WhoAmIExtendedOperation.java                                 |  202 +++++++++++++++++++---
 opends/src/server/org/opends/server/tools/LDAPAuthenticationHandler.java                                    |   28 +-
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/WhoAmIExtendedOperationTestCase.java |  260 ++++++++++++++++++++++++++++
 opends/src/server/org/opends/server/messages/ExtensionsMessages.java                                        |   12 
 4 files changed, 455 insertions(+), 47 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/WhoAmIExtendedOperation.java b/opends/src/server/org/opends/server/extensions/WhoAmIExtendedOperation.java
index 763a50b..ddd61c7 100644
--- a/opends/src/server/org/opends/server/extensions/WhoAmIExtendedOperation.java
+++ b/opends/src/server/org/opends/server/extensions/WhoAmIExtendedOperation.java
@@ -28,20 +28,34 @@
 
 
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.ExtendedOperationHandler;
 import org.opends.server.config.ConfigException;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ExtendedOperation;
+import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
 import org.opends.server.types.InitializationException;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.Privilege;
 import org.opends.server.types.ResultCode;
 
+import static org.opends.server.loggers.debug.DebugLogger.*;
 import static org.opends.server.messages.ExtensionsMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.util.ServerConstants.*;
-import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg;
 
 
 /**
@@ -51,6 +65,10 @@
 public class WhoAmIExtendedOperation
        extends ExtendedOperationHandler<ExtendedOperationHandlerCfg>
 {
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
 
 
 
@@ -62,7 +80,6 @@
   public WhoAmIExtendedOperation()
   {
     super();
-
   }
 
 
@@ -99,6 +116,7 @@
    * Performs any finalization that may be necessary for this extended
    * operation handler.  By default, no finalization is performed.
    */
+  @Override()
   public void finalizeExtendedOperationHandler()
   {
     DirectoryServer.deregisterSupportedExtension(OID_WHO_AM_I_REQUEST);
@@ -109,42 +127,172 @@
 
 
   /**
-   * Processes the provided extended operation.
-   *
-   * @param  operation  The extended operation to be processed.
+   * {@inheritDoc}
    */
+  @Override()
+  public Set<String> getSupportedControls()
+  {
+    HashSet<String> supportedControls = new HashSet<String>(2);
+
+    supportedControls.add(OID_PROXIED_AUTH_V1);
+    supportedControls.add(OID_PROXIED_AUTH_V2);
+
+    return supportedControls;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public void processExtendedOperation(ExtendedOperation operation)
   {
-    // Get the client connection and determine the DN of the user associated
-    // with it.
+    // Process any supported controls for this operation, including the
+    // proxied authorization control.
     ClientConnection clientConnection = operation.getClientConnection();
-    if (clientConnection == null)
+    List<Control> requestControls = operation.getRequestControls();
+    if (requestControls != null)
     {
-      // There is no client connection, so we can't make the determination.
-      operation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+      for (Control c : requestControls)
+      {
+        String oid = c.getOID();
+        if (oid.equals(OID_PROXIED_AUTH_V1))
+        {
+          // The requester must have the PROXIED_AUTH privilige in order to
+          // be able to use this control.
+          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
+                                              operation))
+          {
+            int msgID = MSGID_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
+            operation.appendErrorMessage(getMessage(msgID));
+            operation.setResultCode(ResultCode.AUTHORIZATION_DENIED);
+            return;
+          }
 
-      int msgID = MSGID_EXTOP_WHOAMI_NO_CLIENT_CONNECTION;
-      operation.appendErrorMessage(getMessage(msgID));
-      return;
+
+          ProxiedAuthV1Control proxyControl;
+          if (c instanceof ProxiedAuthV1Control)
+          {
+            proxyControl = (ProxiedAuthV1Control) c;
+          }
+          else
+          {
+            try
+            {
+              proxyControl = ProxiedAuthV1Control.decodeControl(c);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              operation.setResultCode(ResultCode.valueOf(le.getResultCode()));
+              operation.appendErrorMessage(le.getMessage());
+              return;
+            }
+          }
+
+
+          Entry authorizationEntry;
+          try
+          {
+            authorizationEntry = proxyControl.getAuthorizationEntry();
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            operation.setResultCode(de.getResultCode());
+            operation.appendErrorMessage(de.getErrorMessage());
+            return;
+          }
+
+          operation.setAuthorizationEntry(authorizationEntry);
+        }
+        else if (oid.equals(OID_PROXIED_AUTH_V2))
+        {
+          // The requester must have the PROXIED_AUTH privilige in order to
+          // be able to use this control.
+          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
+                                              operation))
+          {
+            int msgID = MSGID_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
+            operation.appendErrorMessage(getMessage(msgID));
+            operation.setResultCode(ResultCode.AUTHORIZATION_DENIED);
+            return;
+          }
+
+
+          ProxiedAuthV2Control proxyControl;
+          if (c instanceof ProxiedAuthV2Control)
+          {
+            proxyControl = (ProxiedAuthV2Control) c;
+          }
+          else
+          {
+            try
+            {
+              proxyControl = ProxiedAuthV2Control.decodeControl(c);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              operation.setResultCode(ResultCode.valueOf(le.getResultCode()));
+              operation.appendErrorMessage(le.getMessage());
+              return;
+            }
+          }
+
+
+          Entry authorizationEntry;
+          try
+          {
+            authorizationEntry = proxyControl.getAuthorizationEntry();
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            operation.setResultCode(de.getResultCode());
+            operation.appendErrorMessage(de.getErrorMessage());
+            return;
+          }
+
+          operation.setAuthorizationEntry(authorizationEntry);
+        }
+      }
     }
 
-    // Get the auth info from the client connection.
-    AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
-    if ((authInfo == null) || (authInfo.getAuthenticationDN() == null))
+
+    // Get the authorization DN for the operation and add it to the response
+    // value.
+    String authzID;
+    DN authzDN = operation.getAuthorizationDN();
+    if (authzDN == null)
     {
-      // The user must not be authenticated, so we can send back an empty
-      // response value.
-      operation.setResultCode(ResultCode.SUCCESS);
-      operation.setResponseValue(new ASN1OctetString());
-      return;
+      authzID = "";
+    }
+    else
+    {
+      authzID = "dn:" + authzDN.toString();
     }
 
-    // Get the DN of the authenticated user and put that in the response.
-    // FIXME -- Do we need to support the use of an authorization ID that is
-    //          different from the authentication ID?
+    operation.setResponseValue(new ASN1OctetString(authzID));
+    operation.appendAdditionalLogMessage("authzID=\"" + authzID + "\"");
     operation.setResultCode(ResultCode.SUCCESS);
-    operation.setResponseValue(new ASN1OctetString("dn:" +
-                                    authInfo.getAuthenticationDN().toString()));
   }
 }
 
diff --git a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index c8cdadb..c4dfdf3 100644
--- a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -2948,10 +2948,10 @@
 
   /**
    * The message ID for the message that will be used if a "Who Am I?" request
-   * is received but no client connection structure is available.  This does not
-   * take any arguments.
+   * includes the proxied authorization control and the client doesn't have
+   * permission to use it.  This does not take any arguments.
    */
-  public static final int MSGID_EXTOP_WHOAMI_NO_CLIENT_CONNECTION =
+  public static final int MSGID_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES =
        CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 277;
 
 
@@ -7155,9 +7155,9 @@
                     "GSSAPI authentication");
 
 
-    registerMessage(MSGID_EXTOP_WHOAMI_NO_CLIENT_CONNECTION,
-                    "No client connection structure is available for use in " +
-                    "determining the requested authorization ID");
+    registerMessage(MSGID_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES,
+                    "You do not have sufficient privileges to use the " +
+                    "proxied authorization control");
 
 
     registerMessage(MSGID_SOFTREFCACHE_DESCRIPTION_LOCK_TIMEOUT,
diff --git a/opends/src/server/org/opends/server/tools/LDAPAuthenticationHandler.java b/opends/src/server/org/opends/server/tools/LDAPAuthenticationHandler.java
index ff929bc..df68e95 100644
--- a/opends/src/server/org/opends/server/tools/LDAPAuthenticationHandler.java
+++ b/opends/src/server/org/opends/server/tools/LDAPAuthenticationHandler.java
@@ -596,7 +596,7 @@
         if (name.equalsIgnoreCase(SASL_PROPERTY_TRACE))
         {
           // This is acceptable, and we'll take any single value.
-          List<String> values = saslProperties.get(SASL_PROPERTY_TRACE);
+          List<String> values = saslProperties.get(name);
           Iterator<String> iterator = values.iterator();
           if (iterator.hasNext())
           {
@@ -848,7 +848,7 @@
 
       if (lowerName.equals(SASL_PROPERTY_AUTHID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -1380,7 +1380,7 @@
 
       if (lowerName.equals(SASL_PROPERTY_AUTHID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -1397,7 +1397,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_REALM))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_REALM);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -1415,7 +1415,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_QOP))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_QOP);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -1453,7 +1453,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_DIGEST_URI))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_DIGEST_URI);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -1470,7 +1470,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_AUTHZID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHZID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -2758,7 +2758,7 @@
 
       if (lowerName.equals(SASL_PROPERTY_AUTHID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -2775,7 +2775,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_AUTHZID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHZID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -2792,7 +2792,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_KDC))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_KDC);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -2809,7 +2809,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_QOP))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_QOP);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -2848,7 +2848,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_REALM))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_REALM);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -3069,7 +3069,7 @@
 
       if (lowerName.equals(SASL_PROPERTY_AUTHID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
@@ -3086,7 +3086,7 @@
       }
       else if (lowerName.equals(SASL_PROPERTY_AUTHZID))
       {
-        List<String> values = saslProperties.get(SASL_PROPERTY_AUTHZID);
+        List<String> values = saslProperties.get(name);
         Iterator<String> iterator = values.iterator();
         if (iterator.hasNext())
         {
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 5dd1dc6..a025686 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
@@ -30,23 +30,31 @@
 
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import org.opends.server.TestCaseUtils;
+import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.AddOperation;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ExtendedOperation;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
+import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
 import org.opends.server.protocols.ldap.LDAPControl;
 import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.protocols.ldap.UnbindRequestProtocolOp;
 import org.opends.server.tools.LDAPAuthenticationHandler;
 import org.opends.server.tools.LDAPReader;
 import org.opends.server.tools.LDAPWriter;
 import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.ResultCode;
 
@@ -265,5 +273,257 @@
     writer.writeMessage(unbindMessage);
     s.close();
   }
+
+
+
+  /**
+   * Tests the use of the "Who Am I?" extended operation when used by a client
+   * that has authenticated using a SASL mechanism and specified an alternate
+   * authorization identity.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testWithAlternateSASLAuthzID()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntries(
+         "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",
+         "",
+         "dn: uid=proxy.user,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "uid: proxy.user",
+         "givenName: Proxy",
+         "sn: User",
+         "cn: Proxy User",
+         "userPassword: password",
+         "ds-privilege-name: bypass-acl",
+         "ds-privilege-name: proxied-auth");
+
+
+    Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+    LDAPReader reader = new LDAPReader(s);
+    LDAPWriter writer = new LDAPWriter(s);
+
+
+    // Bind as the proxy user with an alternate authorization identity, and use
+    // the "Who Am I?" operation.
+    AtomicInteger nextMessageID = new AtomicInteger(1);
+    LDAPAuthenticationHandler authHandler =
+         new LDAPAuthenticationHandler(reader, writer, "localhost",
+                                       nextMessageID);
+
+    HashMap<String,List<String>> saslProperties =
+         new HashMap<String,List<String>>(2);
+
+    ArrayList<String> authIDList = new ArrayList<String>(1);
+    authIDList.add("dn:uid=proxy.user,o=test");
+    saslProperties.put("authID", authIDList);
+
+    ArrayList<String> authzIDList = new ArrayList<String>(1);
+    authzIDList.add("dn:uid=test.user,o=test");
+    saslProperties.put("authzID", authzIDList);
+
+    authHandler.doSASLPlain(new ASN1OctetString(),
+                            new ASN1OctetString("password"), saslProperties,
+                            new ArrayList<LDAPControl>(),
+                            new ArrayList<LDAPControl>());
+    ASN1OctetString authzID = authHandler.requestAuthorizationIdentity();
+    assertNotNull(authzID);
+    assertEquals(authzID.toString(), "dn:uid=test.user,o=test");
+
+
+    // Close the connection to the server.
+    LDAPMessage unbindMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
+                                                new UnbindRequestProtocolOp());
+    writer.writeMessage(unbindMessage);
+    s.close();
+  }
+
+
+
+  /**
+   * Tests the use of the Who Am I? extended operation in conjunction with the
+   * proxied authorization control by an appropriately authorized user.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testWithAllowedProxiedAuthControl()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntries(
+         "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",
+         "",
+         "dn: uid=proxy.user,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "uid: proxy.user",
+         "givenName: Proxy",
+         "sn: User",
+         "cn: Proxy User",
+         "userPassword: password",
+         "ds-privilege-name: bypass-acl",
+         "ds-privilege-name: proxied-auth");
+
+
+    Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+    LDAPReader reader = new LDAPReader(s);
+    LDAPWriter writer = new LDAPWriter(s);
+
+
+    // Bind as the proxy user and use the "Who Am I?" operation, but without the
+    // proxied auth control.
+    AtomicInteger nextMessageID = new AtomicInteger(1);
+    LDAPAuthenticationHandler authHandler =
+         new LDAPAuthenticationHandler(reader, writer, "localhost",
+                                       nextMessageID);
+    authHandler.doSimpleBind(3, new ASN1OctetString("uid=proxy.user,o=test"),
+                             new ASN1OctetString("password"),
+                             new ArrayList<LDAPControl>(),
+                             new ArrayList<LDAPControl>());
+    ASN1OctetString authzID = authHandler.requestAuthorizationIdentity();
+    assertNotNull(authzID);
+    assertEquals(authzID.toString(), "dn:uid=proxy.user,o=test");
+
+
+    // Use the "Who Am I?" operation again, this time with the proxy control.
+    ExtendedRequestProtocolOp extendedRequest =
+         new ExtendedRequestProtocolOp(OID_WHO_AM_I_REQUEST);
+    ArrayList<LDAPControl> requestControls = new ArrayList<LDAPControl>(1);
+    requestControls.add(new LDAPControl(new ProxiedAuthV2Control(
+         new ASN1OctetString("dn:uid=test.user,o=test"))));
+    LDAPMessage message = new LDAPMessage(nextMessageID.getAndIncrement(),
+                                          extendedRequest, requestControls);
+    writer.writeMessage(message);
+
+    message = reader.readMessage();
+    ExtendedResponseProtocolOp extendedResponse =
+         message.getExtendedResponseProtocolOp();
+    assertEquals(extendedResponse.getResultCode(), LDAPResultCode.SUCCESS);
+    authzID = extendedResponse.getValue();
+    assertNotNull(authzID);
+    assertEquals(authzID.toString(), "dn:uid=test.user,o=test");
+
+
+    // Close the connection to the server.
+    message = new LDAPMessage(nextMessageID.getAndIncrement(),
+                              new UnbindRequestProtocolOp());
+    writer.writeMessage(message);
+    s.close();
+  }
+
+
+
+  /**
+   * Tests the use of the Who Am I? extended operation in conjunction with the
+   * proxied authorization control by a user who doesn't have the rights to use
+   * that control.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testWithDisallowedProxiedAuthControl()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntries(
+         "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",
+         "",
+         "dn: uid=cantproxy.user,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "uid: proxy.user",
+         "givenName: Cantproxy",
+         "sn: User",
+         "cn: Cantproxy User",
+         "userPassword: password",
+         "ds-privilege-name: bypass-acl");
+
+
+    Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+    LDAPReader reader = new LDAPReader(s);
+    LDAPWriter writer = new LDAPWriter(s);
+
+
+    // Bind as the proxy user and use the "Who Am I?" operation, but without the
+    // proxied auth control.
+    AtomicInteger nextMessageID = new AtomicInteger(1);
+    LDAPAuthenticationHandler authHandler =
+         new LDAPAuthenticationHandler(reader, writer, "localhost",
+                                       nextMessageID);
+    authHandler.doSimpleBind(3,
+                             new ASN1OctetString("uid=cantproxy.user,o=test"),
+                             new ASN1OctetString("password"),
+                             new ArrayList<LDAPControl>(),
+                             new ArrayList<LDAPControl>());
+    ASN1OctetString authzID = authHandler.requestAuthorizationIdentity();
+    assertNotNull(authzID);
+    assertEquals(authzID.toString(), "dn:uid=cantproxy.user,o=test");
+
+
+    // Use the "Who Am I?" operation again, this time with the proxy control.
+    ExtendedRequestProtocolOp extendedRequest =
+         new ExtendedRequestProtocolOp(OID_WHO_AM_I_REQUEST);
+    ArrayList<LDAPControl> requestControls = new ArrayList<LDAPControl>(1);
+    requestControls.add(new LDAPControl(new ProxiedAuthV2Control(
+         new ASN1OctetString("dn:uid=test.user,o=test"))));
+    LDAPMessage message = new LDAPMessage(nextMessageID.getAndIncrement(),
+                                          extendedRequest, requestControls);
+    writer.writeMessage(message);
+
+    message = reader.readMessage();
+    ExtendedResponseProtocolOp extendedResponse =
+         message.getExtendedResponseProtocolOp();
+    assertEquals(extendedResponse.getResultCode(),
+                 LDAPResultCode.AUTHORIZATION_DENIED);
+    assertNull(extendedResponse.getValue());
+
+
+    // Close the connection to the server.
+    message = new LDAPMessage(nextMessageID.getAndIncrement(),
+                              new UnbindRequestProtocolOp());
+    writer.writeMessage(message);
+    s.close();
+  }
 }
 

--
Gitblit v1.10.0