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

neil_a_wilson
05.25.2007 4ba41d9ca10d151a6c4f80e48c78e322c98d5549
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()));
  }
}
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,
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())
        {
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();
  }
}