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

Matthew Swift
31.17.2011 e695f124cf06c758d6d853ebbb773b07d0b2449f
OPENDJ-262: Implement pass through authentication (PTA)

* stub out more of the LDAP PTA impl
* add initial unit tests for LDAP PTA - these will be enabled when the feature is completed.
1 files added
1 files modified
1358 ■■■■■ changed files
opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java 288 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java 1070 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
@@ -29,6 +29,7 @@
import java.io.Closeable;
import java.util.List;
import org.opends.messages.Message;
@@ -51,52 +52,100 @@
{
  /**
   * LDAP PTA policy state implementation.
   * An LDAP connection which will be used in order to search for or
   * authenticate users.
   */
  private static final class StateImpl extends AuthenticationPolicyState
  static interface Connection extends Closeable
  {
    private final PolicyImpl policy;
    private StateImpl(PolicyImpl policy)
    {
      this.policy = policy;
    }
    /**
     * Closes this connection.
     */
    @Override
    void close();
    /**
     * {@inheritDoc}
     * Returns the name of the user whose entry matches the provided search
     * criteria.
     *
     * @param baseDN
     *          The search base DN.
     * @param scope
     *          The search scope.
     * @param filter
     *          The search filter.
     * @return The name of the user whose entry matches the provided search
     *         criteria, or {@code null} if no matching user entry was found.
     * @throws DirectoryException
     *           If the search returned more than one entry, or if the search
     *           failed unexpectedly.
     */
    public boolean passwordMatches(ByteString password)
        throws DirectoryException
    {
      // TODO: perform PTA here.
      return false;
    }
    ByteString search(DN baseDN, SearchScope scope, SearchFilter filter)
        throws DirectoryException;
    /**
     * {@inheritDoc}
     * Performs a simple bind for the user.
     *
     * @param username
     *          The user name (usually a bind DN).
     * @param password
     *          The user's password.
     * @throws DirectoryException
     *           If the credentials were invalid, or the authentication failed
     *           unexpectedly.
     */
    public AuthenticationPolicy getAuthenticationPolicy()
    {
      return policy;
    }
    void simpleBind(ByteString username, ByteString password)
        throws DirectoryException;
  }
  /**
   * An interface for obtaining connections: users of this interface will obtain
   * a connection, perform a single operation (search or bind), and then close
   * it.
   */
  static interface ConnectionFactory
  {
    /**
     * {@inheritDoc}
     * Returns a connection which can be used in order to search for or
     * authenticate users.
     *
     * @return The connection.
     * @throws DirectoryException
     *           If an unexpected error occurred while attempting to obtain a
     *           connection.
     */
    public void finalizeStateAfterBind() throws DirectoryException
    {
      // TODO: cache password if needed.
    }
    Connection getConnection() throws DirectoryException;
  }
  /**
   * An interface for obtaining a connection factory for LDAP connections to a
   * named LDAP server.
   */
  static interface LDAPConnectionFactoryProvider
  {
    /**
     * Returns a connection factory which can be used for obtaining connections
     * to the specified LDAP server.
     *
     * @param host
     *          The LDAP server host name.
     * @param port
     *          The LDAP server port.
     * @param options
     *          The LDAP connection options.
     * @return A connection factory which can be used for obtaining connections
     *         to the specified LDAP server.
     */
    ConnectionFactory getLDAPConnectionFactory(String host, int port,
        LDAPPassThroughAuthenticationPolicyCfg options);
  }
@@ -108,36 +157,25 @@
      ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg>
  {
    private PolicyImpl(LDAPPassThroughAuthenticationPolicyCfg configuration)
    // Current configuration.
    private LDAPPassThroughAuthenticationPolicyCfg configuration;
    private PolicyImpl(
        final LDAPPassThroughAuthenticationPolicyCfg configuration)
    {
      this.configuration = configuration;
    }
    // Current configuration.
    private LDAPPassThroughAuthenticationPolicyCfg configuration;
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationChangeAcceptable(
        LDAPPassThroughAuthenticationPolicyCfg configuration,
        List<Message> unacceptableReasons)
    {
      // The configuration is always valid.
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ConfigChangeResult applyConfigurationChange(
        LDAPPassThroughAuthenticationPolicyCfg configuration)
        final LDAPPassThroughAuthenticationPolicyCfg configuration)
    {
      // TODO: close and re-open connections if servers have changed.
      this.configuration = configuration;
@@ -149,6 +187,30 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public AuthenticationPolicyState createAuthenticationPolicyState(
        final Entry userEntry, final long time) throws DirectoryException
    {
      return new StateImpl(this);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void finalizeAuthenticationPolicy()
    {
      // TODO: release pooled connections, etc.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public DN getDN()
    {
      return configuration.dn();
@@ -159,20 +221,13 @@
    /**
     * {@inheritDoc}
     */
    public AuthenticationPolicyState createAuthenticationPolicyState(
        Entry userEntry, long time) throws DirectoryException
    @Override
    public boolean isConfigurationChangeAcceptable(
        final LDAPPassThroughAuthenticationPolicyCfg configuration,
        final List<Message> unacceptableReasons)
    {
      return new StateImpl(this);
    }
    /**
     * {@inheritDoc}
     */
    public void finalizeAuthenticationPolicy()
    {
      // TODO: release pooled connections, etc.
      // The configuration is always valid.
      return true;
    }
  }
@@ -180,13 +235,117 @@
  /**
   * LDAP PTA policy state implementation.
   */
  private static final class StateImpl extends AuthenticationPolicyState
  {
    private final PolicyImpl policy;
    private StateImpl(final PolicyImpl policy)
    {
      this.policy = policy;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void finalizeStateAfterBind() throws DirectoryException
    {
      // TODO: cache password if needed.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public AuthenticationPolicy getAuthenticationPolicy()
    {
      return policy;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean passwordMatches(final ByteString password)
        throws DirectoryException
    {
      // TODO: perform PTA here.
      return false;
    }
  }
  // The provider which should be used by policies to create LDAP connections.
  private final LDAPConnectionFactoryProvider provider;
  /**
   * The default LDAP connection factory provider.
   */
  private static final LDAPConnectionFactoryProvider DEFAULT_PROVIDER =
    new LDAPConnectionFactoryProvider()
  {
    @Override
    public ConnectionFactory getLDAPConnectionFactory(final String host,
        final int port, final LDAPPassThroughAuthenticationPolicyCfg options)
    {
      // TODO: not yet implemented.
      return null;
    }
  };
  /**
   * Public default constructor used by the admin framework. This will use the
   * default LDAP connection factory provider.
   */
  public LDAPPassThroughAuthenticationPolicyFactory()
  {
    this(DEFAULT_PROVIDER);
  }
  /**
   * Package private constructor allowing unit tests to provide mock connection
   * implementations.
   *
   * @param provider
   *          The LDAP connection factory provider implementation which LDAP PTA
   *          authentication policies will use.
   */
  LDAPPassThroughAuthenticationPolicyFactory(
      final LDAPConnectionFactoryProvider provider)
  {
    this.provider = provider;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public AuthenticationPolicy createAuthenticationPolicy(
      LDAPPassThroughAuthenticationPolicyCfg configuration)
      final LDAPPassThroughAuthenticationPolicyCfg configuration)
      throws ConfigException, InitializationException
  {
    PolicyImpl policy = new PolicyImpl(configuration);
    final PolicyImpl policy = new PolicyImpl(configuration);
    configuration.addLDAPPassThroughChangeListener(policy);
    return policy;
  }
@@ -196,9 +355,10 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAcceptable(
      LDAPPassThroughAuthenticationPolicyCfg configuration,
      List<Message> unacceptableReasons)
      final LDAPPassThroughAuthenticationPolicyCfg configuration,
      final List<Message> unacceptableReasons)
  {
    // The configuration is always valid.
    return true;
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
New file
@@ -0,0 +1,1070 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2011 ForgeRock AS.
 */
package org.opends.server.extensions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.util.LinkedList;
import java.util.Queue;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.LDAPPassThroughAuthenticationPolicyCfgDefn.MappingPolicy;
import org.opends.server.admin.std.server.AuthenticationPolicyCfg;
import org.opends.server.admin.std.server.LDAPPassThroughAuthenticationPolicyCfg;
import org.opends.server.api.AuthenticationPolicy;
import org.opends.server.api.AuthenticationPolicyState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.Connection;
import org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.ConnectionFactory;
import org.opends.server.types.*;
import org.opends.server.util.StaticUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test LDAP authentication mappingPolicy implementation.
 */
public class LDAPPassThroughAuthenticationPolicyTestCase extends
    ExtensionsTestCase
{
  static class CloseEvent extends Event<Void>
  {
    private final GetConnectionEvent getConnectionEvent;
    CloseEvent(GetConnectionEvent getConnectionEvent)
    {
      this.getConnectionEvent = getConnectionEvent;
    }
    /**
     * {@inheritDoc}
     */
    boolean matchesEvent(Event<?> event)
    {
      if (event instanceof CloseEvent)
      {
        CloseEvent closeEvent = (CloseEvent) event;
        return getConnectionEvent.matchesEvent(closeEvent.getConnectionEvent);
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    StringBuilder toString(StringBuilder builder)
    {
      builder.append("CloseEvent(");
      builder.append(getConnectionEvent);
      builder.append(')');
      return builder;
    }
  }
  static abstract class Event<T>
  {
    public final boolean equals(Object obj)
    {
      if (obj instanceof Event<?>)
      {
        return matchesEvent((Event<?>) obj);
      }
      else
      {
        return false;
      }
    }
    public final String toString()
    {
      StringBuilder builder = new StringBuilder();
      return toString(builder).toString();
    }
    T getResult()
    {
      return null;
    }
    abstract boolean matchesEvent(Event<?> event);
    abstract StringBuilder toString(StringBuilder builder);
  }
  static class GetConnectionEvent extends Event<DirectoryException>
  {
    private final GetLDAPConnectionFactoryEvent fevent;
    private final ResultCode resultCode;
    GetConnectionEvent(GetLDAPConnectionFactoryEvent fevent)
    {
      this(fevent, ResultCode.SUCCESS);
    }
    GetConnectionEvent(GetLDAPConnectionFactoryEvent fevent,
        ResultCode resultCode)
    {
      this.fevent = fevent;
      this.resultCode = resultCode;
    }
    /**
     * {@inheritDoc}
     */
    DirectoryException getResult()
    {
      if (resultCode != ResultCode.SUCCESS)
      {
        return new DirectoryException(resultCode,
            resultCode.getResultCodeName());
      }
      else
      {
        return null;
      }
    }
    /**
     * {@inheritDoc}
     */
    boolean matchesEvent(Event<?> event)
    {
      if (event instanceof GetConnectionEvent)
      {
        GetConnectionEvent getConnectionEvent = (GetConnectionEvent) event;
        return fevent.matchesEvent(getConnectionEvent.fevent);
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    StringBuilder toString(StringBuilder builder)
    {
      builder.append("GetConnectionEvent(");
      builder.append(fevent);
      builder.append(')');
      return builder;
    }
  }
  static class GetLDAPConnectionFactoryEvent extends Event<Void>
  {
    private final String hostPort;
    private final LDAPPassThroughAuthenticationPolicyCfg options;
    GetLDAPConnectionFactoryEvent(String hostPort,
        LDAPPassThroughAuthenticationPolicyCfg options)
    {
      this.hostPort = hostPort;
      this.options = options;
    }
    /**
     * {@inheritDoc}
     */
    boolean matchesEvent(Event<?> event)
    {
      if (event instanceof GetLDAPConnectionFactoryEvent)
      {
        GetLDAPConnectionFactoryEvent providerEvent = (GetLDAPConnectionFactoryEvent) event;
        return hostPort.equals(providerEvent.hostPort)
            && options == providerEvent.options;
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    StringBuilder toString(StringBuilder builder)
    {
      builder.append("GetLDAPConnectionFactoryEvent(");
      builder.append(hostPort);
      builder.append(", ");
      builder.append(options);
      builder.append(')');
      return builder;
    }
  }
  static final class MockConnection implements
      LDAPPassThroughAuthenticationPolicyFactory.Connection
  {
    private final GetConnectionEvent getConnectionEvent;
    private final MockProvider mockProvider;
    MockConnection(MockProvider mockProvider,
        GetConnectionEvent getConnectionEvent)
    {
      this.mockProvider = mockProvider;
      this.getConnectionEvent = getConnectionEvent;
    }
    /**
     * {@inheritDoc}
     */
    public void close()
    {
      CloseEvent event = new CloseEvent(getConnectionEvent);
      mockProvider.assertNextEventExpected(event);
    }
    /**
     * {@inheritDoc}
     */
    public ByteString search(DN baseDN, SearchScope scope, SearchFilter filter)
        throws DirectoryException
    {
      SearchEvent event = new SearchEvent(getConnectionEvent, baseDN, scope,
          filter);
      Object result = mockProvider.assertNextEventExpected(event);
      if (result instanceof ByteString)
      {
        return (ByteString) result;
      }
      else
      {
        throw (DirectoryException) result;
      }
    }
    /**
     * {@inheritDoc}
     */
    public void simpleBind(ByteString username, ByteString password)
        throws DirectoryException
    {
      SimpleBindEvent event = new SimpleBindEvent(getConnectionEvent,
          username.toString(), password.toString());
      DirectoryException e = mockProvider.assertNextEventExpected(event);
      if (e != null) throw e;
    }
  }
  static final class MockFactory implements
      LDAPPassThroughAuthenticationPolicyFactory.ConnectionFactory
  {
    private final GetLDAPConnectionFactoryEvent getLDAPConnectionFactoryEvent;
    private final MockProvider mockProvider;
    MockFactory(MockProvider mockProvider,
        GetLDAPConnectionFactoryEvent providerEvent)
    {
      this.mockProvider = mockProvider;
      this.getLDAPConnectionFactoryEvent = providerEvent;
    }
    /**
     * {@inheritDoc}
     */
    public Connection getConnection() throws DirectoryException
    {
      GetConnectionEvent event = new GetConnectionEvent(
          getLDAPConnectionFactoryEvent);
      DirectoryException e = mockProvider.assertNextEventExpected(event);
      if (e != null)
      {
        throw e;
      }
      else
      {
        return new MockConnection(mockProvider, event);
      }
    }
  }
  final class MockPolicyCfg implements LDAPPassThroughAuthenticationPolicyCfg
  {
    private final SortedSet<DN> baseDNs = new TreeSet<DN>();
    private final SortedSet<AttributeType> mappedAttributes = new TreeSet<AttributeType>();
    private MappingPolicy mappingPolicy = MappingPolicy.UNMAPPED;
    private final SortedSet<String> primaryServers = new TreeSet<String>();
    private final SortedSet<String> secondaryServers = new TreeSet<String>();
    public void addChangeListener(
        ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
    {
      // Do nothing.
    }
    public void addLDAPPassThroughChangeListener(
        ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
    {
      // Do nothing.
    }
    public Class<? extends LDAPPassThroughAuthenticationPolicyCfg> configurationClass()
    {
      return LDAPPassThroughAuthenticationPolicyCfg.class;
    }
    public DN dn()
    {
      return policyDN;
    }
    public long getConnectionTimeout()
    {
      return 3000;
    }
    public String getJavaClass()
    {
      return LDAPPassThroughAuthenticationPolicyFactory.class.getName();
    }
    public SortedSet<AttributeType> getMappedAttribute()
    {
      return mappedAttributes;
    }
    public SortedSet<DN> getMappedSearchBaseDN()
    {
      return baseDNs;
    }
    public DN getMappedSearchBindDN()
    {
      return searchBindDN;
    }
    public String getMappedSearchBindPassword()
    {
      return "searchPassword";
    }
    public MappingPolicy getMappingPolicy()
    {
      return mappingPolicy;
    }
    public SortedSet<String> getPrimaryRemoteLDAPServer()
    {
      return primaryServers;
    }
    public SortedSet<String> getSecondaryRemoteLDAPServer()
    {
      return secondaryServers;
    }
    public SortedSet<String> getSSLCipherSuite()
    {
      return new TreeSet<String>();
    }
    public SortedSet<String> getSSLProtocol()
    {
      return new TreeSet<String>();
    }
    public String getTrustManagerProvider()
    {
      return "ignored";
    }
    public DN getTrustManagerProviderDN()
    {
      return trustManagerDN;
    }
    public boolean isUseSSL()
    {
      return false;
    }
    public boolean isUseTCPKeepAlive()
    {
      return false;
    }
    public boolean isUseTCPNoDelay()
    {
      return false;
    }
    public void removeChangeListener(
        ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
    {
      // Do nothing.
    }
    public void removeLDAPPassThroughChangeListener(
        ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
    {
      // Do nothing.
    }
    MockPolicyCfg withBaseDN(String baseDN)
    {
      try
      {
        baseDNs.add(DN.decode(baseDN));
      }
      catch (DirectoryException e)
      {
        throw new RuntimeException(e);
      }
      return this;
    }
    MockPolicyCfg withMappedAttribute(String atype)
    {
      mappedAttributes.add(DirectoryServer.getAttributeType(
          StaticUtils.toLowerCase(atype), true));
      return this;
    }
    MockPolicyCfg withMappingPolicy(MappingPolicy policy)
    {
      this.mappingPolicy = policy;
      return this;
    }
    MockPolicyCfg withPrimaryServer(String hostPort)
    {
      primaryServers.add(hostPort);
      return this;
    }
    MockPolicyCfg withSecondaryServer(String hostPort)
    {
      secondaryServers.add(hostPort);
      return this;
    }
  }
  static final class MockProvider implements
      LDAPPassThroughAuthenticationPolicyFactory.LDAPConnectionFactoryProvider
  {
    private final Queue<Event<?>> expectedEvents = new LinkedList<Event<?>>();
    /**
     * {@inheritDoc}
     */
    public ConnectionFactory getLDAPConnectionFactory(String host, int port,
        LDAPPassThroughAuthenticationPolicyCfg options)
    {
      GetLDAPConnectionFactoryEvent event = new GetLDAPConnectionFactoryEvent(
          host + ":" + port, options);
      assertNextEventExpected(event);
      return new MockFactory(this, event);
    }
    @SuppressWarnings("unchecked")
    <T> T assertNextEventExpected(Event<T> actualEvent)
    {
      Event<?> expectedEvent = expectedEvents.poll();
      if (expectedEvent == null)
      {
        fail("Unexpected event: " + actualEvent);
      }
      else
      {
        assertEquals(actualEvent, expectedEvent);
      }
      return ((Event<T>) expectedEvent).getResult();
    }
    MockProvider withExpectedEvent(Event<?> expectedEvent)
    {
      expectedEvents.add(expectedEvent);
      return this;
    }
  }
  static class SearchEvent extends Event<Object>
  {
    private final DN baseDN;
    private final GetConnectionEvent cevent;
    private final SearchFilter filter;
    private final ResultCode resultCode;
    private final SearchScope scope;
    private final ByteString username;
    SearchEvent(GetConnectionEvent cevent, DN baseDN, SearchScope scope,
        SearchFilter filter)
    {
      this(cevent, baseDN, scope, filter, null, ResultCode.SUCCESS);
    }
    SearchEvent(GetConnectionEvent cevent, DN baseDN, SearchScope scope,
        SearchFilter filter, ByteString username)
    {
      this(cevent, baseDN, scope, filter, username, ResultCode.SUCCESS);
    }
    SearchEvent(GetConnectionEvent cevent, DN baseDN, SearchScope scope,
        SearchFilter filter, ResultCode resultCode)
    {
      this(cevent, baseDN, scope, filter, null, resultCode);
    }
    private SearchEvent(GetConnectionEvent cevent, DN baseDN,
        SearchScope scope, SearchFilter filter, ByteString username,
        ResultCode resultCode)
    {
      this.cevent = cevent;
      this.baseDN = baseDN;
      this.scope = scope;
      this.filter = filter;
      this.username = username;
      this.resultCode = resultCode;
    }
    /**
     * {@inheritDoc}
     */
    Object getResult()
    {
      return resultCode == ResultCode.SUCCESS ? username
          : new DirectoryException(resultCode, resultCode.getResultCodeName());
    }
    /**
     * {@inheritDoc}
     */
    boolean matchesEvent(Event<?> event)
    {
      if (event instanceof SearchEvent)
      {
        SearchEvent searchEvent = (SearchEvent) event;
        return cevent.matchesEvent(searchEvent.cevent)
            && baseDN.equals(searchEvent.baseDN)
            && scope.equals(searchEvent.scope)
            && filter.equals(searchEvent.filter);
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    StringBuilder toString(StringBuilder builder)
    {
      builder.append("SearchEvent(");
      builder.append(cevent);
      builder.append(", ");
      builder.append(baseDN);
      builder.append(", ");
      builder.append(scope);
      builder.append(", ");
      builder.append(filter);
      builder.append(')');
      return builder;
    }
  }
  static class SimpleBindEvent extends Event<DirectoryException>
  {
    private final GetConnectionEvent cevent;
    private final String password;
    private final ResultCode resultCode;
    private final String username;
    SimpleBindEvent(GetConnectionEvent cevent, String username, String password)
    {
      this(cevent, username, password, ResultCode.SUCCESS);
    }
    SimpleBindEvent(GetConnectionEvent cevent, String username,
        String password, ResultCode resultCode)
    {
      this.cevent = cevent;
      this.username = username;
      this.password = password;
      this.resultCode = resultCode;
    }
    /**
     * {@inheritDoc}
     */
    DirectoryException getResult()
    {
      if (resultCode != ResultCode.SUCCESS)
      {
        return new DirectoryException(resultCode,
            resultCode.getResultCodeName());
      }
      else
      {
        return null;
      }
    }
    /**
     * {@inheritDoc}
     */
    boolean matchesEvent(Event<?> event)
    {
      if (event instanceof SimpleBindEvent)
      {
        SimpleBindEvent simpleBindEvent = (SimpleBindEvent) event;
        return cevent.matchesEvent(simpleBindEvent.cevent)
            && username.equals(simpleBindEvent.username)
            && password.equals(simpleBindEvent.password);
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    StringBuilder toString(StringBuilder builder)
    {
      builder.append("SimpleBindEvent(");
      builder.append(cevent);
      builder.append(", ");
      builder.append(username);
      builder.append(", ");
      builder.append(password);
      builder.append(')');
      return builder;
    }
  }
  private final String phost1 = "phost1:11";
  private final String phost2 = "phost2:22";
  private final String phost3 = "phost3:33";
  private final String shost1 = "shost1:11";
  private final String shost2 = "shost2:22";
  private final String shost3 = "shost3:33";
  private DN policyDN;
  private final String policyDNString = "cn=test mappingPolicy,o=test";
  private DN searchBindDN;
  private final String searchBindDNString = "cn=search bind dn";
  private DN trustManagerDN;
  private final String trustManagerDNString = "cn=ignored";
  private final String adDNString = "cn=ad user,o=ad";
  private final String opendjDNString = "cn=test user,o=opendj";
  private Entry userEntry;
  private final String userPassword = "password";
  /**
   * Ensures that the Directory Server is running and creates a test backend
   * containing a single test user.
   *
   * @throws Exception
   *           If an unexpected problem occurs.
   */
  @BeforeClass()
  public void beforeClass() throws Exception
  {
    TestCaseUtils.startServer();
    policyDN = DN.decode(policyDNString);
    trustManagerDN = DN.decode(trustManagerDNString);
    searchBindDN = DN.decode(searchBindDNString);
    userEntry = TestCaseUtils.makeEntry(
        /* @formatter:off */
        "dn: " + opendjDNString,
        "objectClass: top",
        "objectClass: person",
        "sn: user",
        "cn: test user",
        "aduser: " + adDNString,
        "uid: aduser"
        /* @formatter:on */
    );
  }
  /**
   * Tests that initial connection errors are handled properly.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testInitialConnectionFailure() throws Exception
  {
    LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg().withPrimaryServer(
        phost1);
    GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
        phost1, cfg);
    GetConnectionEvent ce = new GetConnectionEvent(fe, ResultCode.UNAVAILABLE);
    MockProvider provider = new MockProvider().withExpectedEvent(fe)
        .withExpectedEvent(ce);
    LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
        provider);
    assertTrue(factory.isConfigurationAcceptable(cfg, null));
    AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
    AuthenticationPolicyState state = policy
        .createAuthenticationPolicyState(userEntry);
    assertEquals(state.getAuthenticationPolicy(), policy);
    try
    {
      state.passwordMatches(ByteString.valueOf(userPassword));
      fail("password match unexpectedly succeeded");
    }
    catch (DirectoryException e)
    {
      assertEquals(e.getResultCode(), ResultCode.UNAVAILABLE);
    }
    state.finalizeStateAfterBind();
    policy.finalizeAuthenticationPolicy();
  }
  /**
   * Tests the unmapped policy where the connection fails during bind.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testUnmappedPolicyConnectionFailure() throws Exception
  {
    LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg().withPrimaryServer(
        phost1);
    GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
        phost1, cfg);
    GetConnectionEvent ce = new GetConnectionEvent(fe);
    MockProvider provider = new MockProvider()
        .withExpectedEvent(fe)
        .withExpectedEvent(ce)
        .withExpectedEvent(
            new SimpleBindEvent(ce, opendjDNString, userPassword,
                ResultCode.UNAVAILABLE)).withExpectedEvent(new CloseEvent(ce));
    LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
        provider);
    assertTrue(factory.isConfigurationAcceptable(cfg, null));
    AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
    AuthenticationPolicyState state = policy
        .createAuthenticationPolicyState(userEntry);
    assertEquals(state.getAuthenticationPolicy(), policy);
    try
    {
      state.passwordMatches(ByteString.valueOf(userPassword));
      fail("password match unexpectedly succeeded");
    }
    catch (DirectoryException e)
    {
      assertEquals(e.getResultCode(), ResultCode.UNAVAILABLE);
    }
    state.finalizeStateAfterBind();
    policy.finalizeAuthenticationPolicy();
  }
  /**
   * Tests the unmapped policy with invalid credentials.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testUnmappedPolicyInvalidCredentials() throws Exception
  {
    LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg().withPrimaryServer(
        phost1);
    GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
        phost1, cfg);
    GetConnectionEvent ce = new GetConnectionEvent(fe);
    MockProvider provider = new MockProvider()
        .withExpectedEvent(fe)
        .withExpectedEvent(ce)
        .withExpectedEvent(
            new SimpleBindEvent(ce, opendjDNString, userPassword,
                ResultCode.INVALID_CREDENTIALS))
        .withExpectedEvent(new CloseEvent(ce));
    LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
        provider);
    assertTrue(factory.isConfigurationAcceptable(cfg, null));
    AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
    AuthenticationPolicyState state = policy
        .createAuthenticationPolicyState(userEntry);
    assertEquals(state.getAuthenticationPolicy(), policy);
    assertFalse(state.passwordMatches(ByteString.valueOf(userPassword)));
    state.finalizeStateAfterBind();
    policy.finalizeAuthenticationPolicy();
  }
  /**
   * Tests the unmapped policy with valid credentials.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testUnmappedPolicyValidCredentials() throws Exception
  {
    LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg().withPrimaryServer(
        phost1);
    GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
        phost1, cfg);
    GetConnectionEvent ce = new GetConnectionEvent(fe);
    MockProvider provider = new MockProvider()
        .withExpectedEvent(fe)
        .withExpectedEvent(ce)
        .withExpectedEvent(
            new SimpleBindEvent(ce, opendjDNString, userPassword))
        .withExpectedEvent(new CloseEvent(ce));
    LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
        provider);
    assertTrue(factory.isConfigurationAcceptable(cfg, null));
    AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
    AuthenticationPolicyState state = policy
        .createAuthenticationPolicyState(userEntry);
    assertEquals(state.getAuthenticationPolicy(), policy);
    assertTrue(state.passwordMatches(ByteString.valueOf(userPassword)));
    state.finalizeStateAfterBind();
    policy.finalizeAuthenticationPolicy();
  }
  private MockPolicyCfg mockCfg()
  {
    return new MockPolicyCfg();
  }
}