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

Chris Ridd
04.53.2015 74d7af9059994d7c6e1b08316429b8dcb017a70b
FR-721 OPENDJ-2071 improve aci checks for proxy auth controls
1 files added
24 files modified
865 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java 32 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java 30 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java 17 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java 16 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java 16 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java 16 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java 14 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java 14 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java 11 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java 23 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java 42 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java 54 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java 42 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java 42 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java 41 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java 40 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 101 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/protocol.properties 2 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java 26 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java 211 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -118,18 +118,6 @@
    private Entry authorizationEntry;
    /**
     * Used to save the current authorization entry when the authorization
     * entry is switched during a proxy access check.
     */
    private final Entry saveAuthorizationEntry;
    /**
     * This entry is only used if proxied authorization is being used.  It is
     * the original authorization entry before the proxied authorization change.
     */
    private Entry origAuthorizationEntry;
    /**
     * True if proxied authorization is being used.
     */
    private boolean proxiedAuthorization;
@@ -248,8 +236,7 @@
      //If the proxied authorization control was processed, then the operation
      //will contain an attachment containing the original authorization entry.
      this.origAuthorizationEntry =
                      (Entry) operation.getAttachment(ORIG_AUTH_ENTRY);
      final Entry origAuthorizationEntry = (Entry) operation.getAttachment(ORIG_AUTH_ENTRY);
      this.proxiedAuthorization = origAuthorizationEntry != null;
      this.authorizationEntry=operation.getAuthorizationEntry();
@@ -292,7 +279,6 @@
      //Reference the current authorization entry, so it can be put back
      //if an access proxy check was performed.
      this.saveAuthorizationEntry=this.authorizationEntry;
      this.rightsMask = rights;
    }
@@ -312,7 +298,6 @@
        this.clientConnection=operation.getClientConnection();
        this.authInfo = authInfo;
        this.authorizationEntry = authInfo.getAuthorizationEntry();
        this.saveAuthorizationEntry=this.authorizationEntry;
        this.rightsMask = rights;
    }
  /**
@@ -489,21 +474,6 @@
     return this.authzid.equals(this.authorizationEntry.getName());
    }
  /**
   * If the specified value is true, then the original authorization entry,
   * which is the  entry before the switch performed by the proxied
   * authorization control processing should be set to the current
   * authorization entry. If the specified value is false then the proxied
   * authorization entry is switched back using the saved copy.
   * @param val The value used to select the authorization entry to use.
   */
    public void useOrigAuthorizationEntry(boolean val) {
      if(val)
        authorizationEntry=origAuthorizationEntry;
      else
        authorizationEntry=saveAuthorizationEntry;
    }
    /** {@inheritDoc} */
    @Override
    public void setDenyList(List<Aci> denys) {
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -559,36 +559,6 @@
      }
    }
    // Check proxy authorization only if the entry has not already been
    // processed (working on a new entry). If working on a new entry,
    // then only do a proxy check if the right is not set to ACI_PROXY
    // and the proxied authorization control has been decoded.
    if (!container.hasSeenEntry())
    {
      if (container.isProxiedAuthorization()
          && !container.hasRights(ACI_PROXY)
          && !container.hasRights(ACI_SKIP_PROXY_CHECK))
      {
        int currentRights = container.getRights();
        // Save the current rights so they can be put back if on success.
        container.setRights(ACI_PROXY);
        // Switch to the original authorization entry, not the proxied one.
        container.useOrigAuthorizationEntry(true);
        if (!accessAllowed(container))
        {
          return false;
        }
        // Access is ok, put the original rights back.
        container.setRights(currentRights);
        // Put the proxied authorization entry back to the current
        // authorization entry.
        container.useOrigAuthorizationEntry(false);
      }
      // Set the seen flag so proxy processing is not performed for this
      // entry again.
      container.setSeenEntry(true);
    }
    // First get all allowed candidate ACIs.
    List<Aci> candidates = aciList.getCandidateAcis(dn);
    /*
opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java
@@ -97,6 +97,23 @@
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
  }
  /** {@inheritDoc} */
  @Override
  public final OperationType getOperationType()
  {
    // Note that no debugging will be done in this method because it is a likely
opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 ForgeRock AS
 *      Portions Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -150,18 +150,4 @@
    return getOperation().toString();
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2007-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 ForgeRock AS
 *      Portions Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -257,6 +257,19 @@
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
  }
  /** {@inheritDoc} */
  @Override
  public final AuthenticationType getAuthenticationType()
  {
    return authType;
opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java
@@ -140,19 +140,4 @@
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 ForgeRock AS
 *      Portions Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -76,18 +76,4 @@
    return getOperation().toString();
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java
@@ -147,6 +147,22 @@
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
  }
  /** {@inheritDoc} */
  @Override
  public final ByteString getRequestValue()
  {
    return requestValue;
opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 ForgeRock AS
 *      Portions Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -94,12 +94,6 @@
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN() {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public ByteString getRawEntryDN() {
    return getOperation().getRawEntryDN();
  }
@@ -148,13 +142,6 @@
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN dn)
  {
    getOperation().setProxiedAuthorizationDN(dn);
  }
  /** {@inheritDoc} */
  @Override
  public DN getNewDN()
  {
    return getOperation().getNewDN();
opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -114,17 +114,4 @@
    return getOperation().toString();
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN){
    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java
@@ -160,6 +160,20 @@
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return operation.getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    operation.setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
  /** {@inheritDoc} */
  @Override
  public CancelRequest getCancelRequest()
  {
    return operation.getCancelRequest();
opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS
 */
package org.opends.server.core;
@@ -370,17 +370,4 @@
    return getOperation().sendSearchReference(reference);
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return getOperation().getProxiedAuthorizationDN();
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN){
    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java
@@ -78,6 +78,20 @@
    // candidate for being called by the logging subsystem.
    return OperationType.UNBIND;
  }
  /** {@inheritDoc} */
  @Override
  public DN getProxiedAuthorizationDN()
  {
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
  }
  /** {@inheritDoc} */
  @Override
  public final List<Control> getResponseControls()
opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java
@@ -35,12 +35,14 @@
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.ExtendedOperation;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.types.*;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ByteString;
import static org.opends.messages.ExtensionMessages.*;
import static org.opends.messages.ProtocolMessages.ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED;
import static org.opends.server.util.ServerConstants.*;
/**
@@ -111,6 +113,15 @@
          authorizationEntry = proxyControlV1.getAuthorizationEntry();
        }
        // Check the requester has the authz user in scope of their proxy aci.
        if (! AccessControlConfigManager.getInstance().getAccessControlHandler()
                .mayProxy(clientConnection.getAuthenticationInfo().getAuthenticationEntry(),
                        authorizationEntry, operation))
        {
          final DN dn = authorizationEntry.getName();
          throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
              ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED.get(dn));
        }
        operation.setAuthorizationEntry(authorizationEntry);
      }
    }
opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS.
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.opends.server.types;
@@ -459,6 +459,27 @@
  DN getAuthorizationDN();
  /**
   * Retrieves the proxied authorization DN for this operation if proxied
   * authorization has been requested.
   *
   * @return  The proxied authorization DN for this operation if proxied
   *          authorization has been requested, or {@code null} if proxied
   *          authorization has not been requested.
   */
  DN getProxiedAuthorizationDN();
  /**
   * Set the proxied authorization DN for this operation if proxied
   * authorization has been requested.
   *
   * @param proxiedAuthorizationDN
   *          The proxied authorization DN for this operation if proxied
   *          authorization has been requested, or {@code null} if proxied
   *          authorization has not been requested.
   */
  void setProxiedAuthorizationDN(DN proxiedAuthorizationDN);
  /**
   * Retrieves the set of attachments defined for this operation, as a
   * mapping between the attachment name and the associated object.
   *
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -49,8 +49,6 @@
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationWrapper;
@@ -60,7 +58,6 @@
import org.opends.server.core.PluginConfigManager;
import org.opends.server.schema.AuthPasswordSyntax;
import org.opends.server.schema.UserPasswordSyntax;
import org.opends.server.types.AdditionalLogItem;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
@@ -1106,44 +1103,9 @@
          postReadRequest =
                getRequestControl(LDAPPostReadRequestControl.DECODER);
        }
        else if (OID_PROXIED_AUTH_V1.equals(oid))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (!getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
              getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
        }
        else if (OID_PROXIED_AUTH_V2.equals(oid))
        {
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
                                                   this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
          continue;
        }
        else if (OID_PASSWORD_POLICY_CONTROL.equals(oid))
        {
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -35,8 +35,6 @@
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.*;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.types.*;
@@ -414,57 +412,9 @@
                ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get(entryDN, de.getMessageObject()));
          }
        }
        else if (oid.equals(OID_PROXIED_AUTH_V1))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
                getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          if (authorizationEntry == null)
          {
            setProxiedAuthorizationDN(DN.rootDN());
          }
          else
          {
            setProxiedAuthorizationDN(authorizationEntry.getName());
          }
        }
        else if (oid.equals(OID_PROXIED_AUTH_V2))
        {
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          if (authorizationEntry == null)
          {
            setProxiedAuthorizationDN(DN.rootDN());
          }
          else
          {
            setProxiedAuthorizationDN(authorizationEntry.getName());
          }
          continue;
        }
        // NYI -- Add support for additional controls.
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -38,21 +38,17 @@
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DeleteOperationWrapper;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PersistentSearch;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.types.AdditionalLogItem;
import org.opends.server.types.CanceledOperationException;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Privilege;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.LockManager.DNLock;
@@ -470,43 +466,9 @@
          preReadRequest =
                getRequestControl(LDAPPreReadRequestControl.DECODER);
        }
        else if (OID_PROXIED_AUTH_V1.equals(oid))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
              getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
        }
        else if (OID_PROXIED_AUTH_V2.equals(oid))
        {
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
          continue;
        }
        // NYI -- Add support for additional controls.
        else if (c.isCritical()
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -44,15 +44,12 @@
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyDNOperationWrapper;
import org.opends.server.core.PersistentSearch;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.types.AdditionalLogItem;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Attributes;
@@ -62,7 +59,6 @@
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.Privilege;
import org.opends.server.types.RDN;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SynchronizationProviderResult;
@@ -625,43 +621,9 @@
            iter.set(postReadRequest);
          }
        }
        else if (OID_PROXIED_AUTH_V1.equals(oid))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
              getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
        }
        else if (OID_PROXIED_AUTH_V2.equals(oid))
        {
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
          continue;
        }
        else if (c.isCritical()
            && (backend == null || !backend.supportsControl(oid)))
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -53,8 +53,6 @@
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
@@ -68,7 +66,6 @@
import org.opends.server.types.AcceptRejectWarn;
import org.opends.server.types.AccountStatusNotification;
import org.opends.server.types.AccountStatusNotificationType;
import org.opends.server.types.AdditionalLogItem;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
@@ -741,43 +738,9 @@
            iter.set(postReadRequest);
          }
        }
        else if (OID_PROXIED_AUTH_V1.equals(oid))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
                  getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
        }
        else if (OID_PROXIED_AUTH_V2.equals(oid))
        {
          // The requester must have the PROXIED_AUTH privilege in order to
          // be able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
          continue;
        }
        else if (OID_PASSWORD_POLICY_CONTROL.equals(oid))
        {
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
@@ -377,43 +377,9 @@
                                de.getMessageObject()), de);
          }
        }
        else if (OID_PROXIED_AUTH_V1.equals(oid))
        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
        {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
              "obsoleteProxiedAuthzV1Control"));
          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
              getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
        }
        else if (OID_PROXIED_AUTH_V2.equals(oid))
        {
          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getName(authorizationEntry));
          continue;
        }
        else if (OID_PERSISTENT_SEARCH.equals(oid))
        {
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -45,10 +45,14 @@
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.LDAPPreReadResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.*;
import org.opends.server.types.*;
import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.ProtocolMessages.ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class defines a local backend workflow element; e-g an entity that
@@ -312,8 +316,20 @@
      for (Iterator<Control> iter = requestControls.iterator(); iter.hasNext();)
      {
        final Control control = iter.next();
        // The aci check for the proxy auth controls needs to check the authentication DN,
        // not the operation target.
        // TODO should we check the authentication DN for all controls?
        final DN aciTarget;
        if (OID_PROXIED_AUTH_V1.equals(control.getOID()) || OID_PROXIED_AUTH_V2.equals(control.getOID()))
        {
          aciTarget= op.getClientConnection().getAuthenticationInfo().getAuthenticationDN();
        }
        else
        {
          aciTarget = targetDN;
        }
        if (!getAccessControlHandler().isAllowed(targetDN, op, control))
        if (!getAccessControlHandler().isAllowed(aciTarget, op, control))
        {
          // As per RFC 4511 4.1.11.
          if (control.isCritical())
@@ -332,6 +348,89 @@
  }
  /**
   * Check the requester has the PROXIED_AUTH privilege in order to be able to use a proxy auth control.
   *
   * @param operation  The operation being checked
   * @throws DirectoryException  If insufficient privileges are detected
   */
  static void checkPrivilegeForProxyAuthControl(Operation operation) throws DirectoryException
  {
    if (! operation.getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, operation))
    {
      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
              ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
    }
  }
  /**
   * Check the requester has the authorization user in scope of proxy aci.
   *
   * @param operation  The operation being checked
   * @param authorizationEntry  The entry being authorized as (e.g. from a proxy auth control)
   * @throws DirectoryException  If no proxy permission is allowed
   */
  static void checkAciForProxyAuthControl(Operation operation, Entry authorizationEntry) throws DirectoryException
  {
    if (! AccessControlConfigManager.getInstance().getAccessControlHandler()
            .mayProxy(operation.getClientConnection().getAuthenticationInfo().getAuthenticationEntry(),
                    authorizationEntry, operation))
    {
      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
              ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED.get(authorizationEntry.getName()));
    }
  }
  /**
   * Process the operation control with the given oid if it is a proxy auth control.
   *
   * Privilege and initial aci checks on the authenticating user are performed. The authenticating
   * user must have the proxied-auth privilege, and the authz user must be in the scope of aci
   * allowing the proxy right to the authenticating user.
   *
   * @param operation  The operation containing the control(s)
   * @param oid  The OID of the detected proxy auth control
   * @return <code>true</code> if the control has been processed, <code>false</code> if not
   * @throws DirectoryException
   */
  static boolean processProxyAuthControls(Operation operation, String oid)
          throws DirectoryException
  {
    final Entry authorizationEntry;
    if (OID_PROXIED_AUTH_V1.equals(oid))
    {
      final ProxiedAuthV1Control proxyControlV1 = operation.getRequestControl(ProxiedAuthV1Control.DECODER);
      // Log usage of legacy proxy authz V1 control.
      operation.addAdditionalLogItem(AdditionalLogItem.keyOnly(operation.getClass(),
              "obsoleteProxiedAuthzV1Control"));
      checkPrivilegeForProxyAuthControl(operation);
      authorizationEntry = proxyControlV1.getAuthorizationEntry();
    }
    else if (OID_PROXIED_AUTH_V2.equals(oid))
    {
      final ProxiedAuthV2Control proxyControlV2 = operation.getRequestControl(ProxiedAuthV2Control.DECODER);
      checkPrivilegeForProxyAuthControl(operation);
      authorizationEntry = proxyControlV2.getAuthorizationEntry();
    }
    else
    {
      return false;
    }
    checkAciForProxyAuthControl(operation, authorizationEntry);
    operation.setAuthorizationEntry(authorizationEntry);
    if (authorizationEntry == null)
    {
      operation.setProxiedAuthorizationDN(DN.NULL_DN);
    }
    else
    {
      operation.setProxiedAuthorizationDN(authorizationEntry.getName());
    }
    return true;
  }
  /**
   * Returns a new {@link DirectoryException} built from the provided
   * resultCodes and messages. Depending on whether ACIs prevent information
   * disclosure, the provided resultCode and message will be masked and
opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
@@ -909,3 +909,5 @@
 contain a valid IdentifiedChoiceValue at the current position: %s
INFO_NULL_KEY_PROVIDER_MANAGER_1524=The keystore %s seems to be missing, \
  this may render the secure port inoperative for '%s'
ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED_1525=Authorization as '%s' specified in \
 the proxied authorization control is not permitted
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java
@@ -432,6 +432,32 @@
      Assert.assertEquals(LDAPResultCode.SUCCESS, expectedRc, "");
  }
  void proxyModify(String ldif, String bindDn, String bindPassword,
      String proxyUser, int expectedRc)
      throws IOException
  {
      File tempFile = getTemporaryLdifFile();
      TestCaseUtils.writeFile(tempFile, ldif);
      ArrayList<String> argList=new ArrayList<>();
      argList.add("-h");
      argList.add("127.0.0.1");
      argList.add("-p");
      argList.add(String.valueOf(TestCaseUtils.getServerLdapPort()));
      argList.add("-D");
      argList.add(bindDn);
      argList.add("-w");
      argList.add(bindPassword);
      if (proxyUser != null) {
          argList.add("-Y");
          argList.add("dn:" + proxyUser);
      }
      argList.add("-f");
      argList.add(tempFile.getAbsolutePath());
      String[] args = new String[argList.size()];
      ldapModify(argList.toArray(args), expectedRc);
  }
  private void ldapModify(String[] args, int expectedRc)
  {
    oStream.reset();
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java
New file
@@ -0,0 +1,211 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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
 *
 *
 *      Copyright 2015 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
import org.opends.server.TestCaseUtils;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.types.Entry;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.List;
import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
/**
 * This class tests ACI behavior with the proxy auth control.
 */
public class ProxyTestCase extends AciTestCase
{
    static final String TEST_BASE = "o=test";
    static final String ALICE_DN = "uid=alice,ou=People," + TEST_BASE;
    static final String BOB_DN = "uid=bob,ou=People," + TEST_BASE;
    static final String CHARLIE_DN = "uid=charlie,ou=People," + TEST_BASE;
    static final String PASSWORD = "password";
    @BeforeClass
    public void setupClass() throws Exception
    {
        deleteAttrFromAdminEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
        TestCaseUtils.initializeTestBackend(true);
        TestCaseUtils.addEntries(
                "dn: ou=People," + TEST_BASE,
                "objectClass: top",
                "objectClass: organizationalUnit",
                "ou: People",
                "aci: (targetcontrol=\"2.16.840.1.113730.3.4.18\")" +
                        "(version 3.0; acl \"Allow proxy auth control\"; " +
                        "allow (read) userdn = \"ldap:///" + ALICE_DN + "\";)",
                "aci: (target=\"ldap:///" + CHARLIE_DN + "\")(targetattr = \"telephoneNumber\")" +
                        "(version 3.0; acl \"Allow Bob to write Charlie\"; " +
                        "allow (write) userdn = \"ldap:///" + BOB_DN + "\";)",
                "aci: (target=\"ldap:///ou=People," + TEST_BASE + "\")" +
                        "(version 3.0; acl \"Allow Alice to proxy People\"; " +
                        "allow (proxy) userdn = \"ldap:///" + ALICE_DN + "\";)",
                "",
                "dn: " + ALICE_DN,
                "objectClass: top",
                "objectClass: person",
                "objectClass: organizationalPerson",
                "objectClass: inetOrgPerson",
                "uid: alice",
                "cn: Alice",
                "sn: User",
                "ds-privilege-name: proxied-auth",
                "userPassword: " + PASSWORD,
                "",
                "dn: uid=bob,ou=People," + TEST_BASE,
                "objectClass: top",
                "objectClass: person",
                "objectClass: organizationalPerson",
                "objectClass: inetOrgPerson",
                "uid: bob",
                "cn: Bob",
                "sn: User",
                "userPassword: " + PASSWORD,
                "",
                "dn: uid=charlie,ou=People," + TEST_BASE,
                "objectClass: top",
                "objectClass: person",
                "objectClass: organizationalPerson",
                "objectClass: inetOrgPerson",
                "uid: charlie",
                "cn: Charlie",
                "sn: User",
                "userPassword: " + PASSWORD,
                "",
                "dn: ou=Groups," + TEST_BASE,
                "objectClass: top",
                "objectClass: organizationalUnit",
                "ou: Groups",
                "",
                "dn: cn=Writable by Bob,ou=Groups," + TEST_BASE,
                "objectClass: top",
                "objectClass: groupOfEntries",
                "cn: Writable by Bob",
                "aci: (targetattr=\"description\")" +
                        "(version 3.0; acl \"Bob writes\"; " +
                        "allow(write) userdn=\"ldap:///" + BOB_DN + "\";)",
                "",
                "dn: cn=Visible to Bob,ou=Groups," + TEST_BASE,
                "objectClass: top",
                "objectClass: groupOfEntries",
                "cn: Visible to Bob",
                "description: Bob can read this group",
                "aci: (targetattr=\"*||+\")" +
                        "(version 3.0; acl \"Bob visible\"; " +
                        "allow(read,search) userdn=\"ldap:///" + BOB_DN + "\";)",
                "",
                "dn: cn=Invisible to Bob,ou=Groups," + TEST_BASE,
                "objectClass: top",
                "objectClass: groupOfEntries",
                "cn: Invisible to Bob",
                "description: Bob cannot see this group",
                "aci: (targetattr=\"*||+\")" +
                        "(version 3.0; acl \"Bob invisible\"; " +
                        "deny(read,search) userdn=\"ldap:///" + BOB_DN + "\";)",
                "");
    }
    @BeforeMethod
    public void clearBackend() throws Exception
    {
        deleteAttrFromAdminEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
    }
    /**
     * Test Alice cannot proxy as Root.
     *
     * @throws Exception If an unexpected result is returned.
     */
    @Test
    public void testProxyAsRoot() throws Exception
    {
        proxyModify(TestCaseUtils.makeLdif(
            "dn: " + CHARLIE_DN,
            "changetype: modify",
            "replace: telephoneNumber",
            "telephoneNumber: 999"),
            ALICE_DN, PASSWORD,
            DIR_MGR_DN,
            LDAPResultCode.AUTHORIZATION_DENIED);
    }
    /**
     * Test Alice (as Bob) modifies Charlie.
     *
     * @throws Exception If an unexpected result is returned.
     */
    @Test
    public void testSimpleProxy() throws Exception
    {
        proxyModify(TestCaseUtils.makeLdif(
             "dn: " + CHARLIE_DN,
             "changetype: modify",
             "replace: telephoneNumber",
             "telephoneNumber: 999"),
             ALICE_DN, PASSWORD,
             BOB_DN,
             LDAPResultCode.SUCCESS);
    }
    /**
     * Test Alice (as Bob) modifies an entry outside of the proxy target scope.
     *
     * @throws Exception If an unexpected result is returned.
     */
    @Test
    public void testUpdateGroup() throws Exception
    {
        proxyModify(TestCaseUtils.makeLdif(
             "dn: cn=Writable by Bob,ou=Groups," + TEST_BASE,
             "changetype: modify",
             "replace: description",
             "description: written by Alice (Bob)"),
             ALICE_DN, PASSWORD,
             BOB_DN,
             LDAPResultCode.SUCCESS);
    }
    /**
     * Test Alice (as Bob) can see entries that Bob can see.
     * Only "cn=Visible to Bob,ou=Groups,..." should be returned.
     *
     * @throws Exception If an unexpected result is returned.
     */
    @Test
    public void testProxiedSearch() throws Exception
    {
        String results = LDAPSearchParams(ALICE_DN, PASSWORD, BOB_DN, null, null,
                TEST_BASE, "(&)", null,
                false, false, LDAPResultCode.SUCCESS);
        List<Entry> entries = TestCaseUtils.entriesFromLdifString(results);
        Assert.assertEquals(entries.size(), 1, "Wrong number of results");
    }
}