From 2df81db54af8853875450098c3f97e16e09d06a8 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 08 Sep 2011 15:56:50 +0000
Subject: [PATCH] Issue OPENDJ-262: Implement pass through authentication (PTA)
---
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java | 1110 ++++++++++++++++++++++++++++++++++++++----------------
opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java | 42 +
2 files changed, 814 insertions(+), 338 deletions(-)
diff --git a/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java b/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
index a27f7b8..3412387 100644
--- a/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
+++ b/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
@@ -184,8 +184,10 @@
/**
* The PTA design guarantees that connections are only used by a single thread
* at a time, so we do not need to perform any synchronization.
+ * <p>
+ * Package private for testing.
*/
- private static final class LDAPConnectionFactory implements ConnectionFactory
+ static final class LDAPConnectionFactory implements ConnectionFactory
{
/**
* LDAP connection implementation.
@@ -196,7 +198,7 @@
private final Socket ldapSocket;
private final LDAPWriter writer;
private final LDAPReader reader;
- private int nextMessageID = 0;
+ private int nextMessageID = 1;
private boolean isClosed = false;
@@ -506,9 +508,25 @@
}
catch (final ASN1Exception e)
{
- throw new DirectoryException(ResultCode.CLIENT_SIDE_DECODING_ERROR,
- ERR_LDAP_PTA_CONNECTION_DECODE_ERROR.get(host, port,
- String.valueOf(options.dn()), e.getMessage()), e);
+ // ASN1 layer hides all underlying IO exceptions.
+ if (e.getCause() instanceof SocketTimeoutException)
+ {
+ throw new DirectoryException(ResultCode.CLIENT_SIDE_TIMEOUT,
+ ERR_LDAP_PTA_CONNECTION_TIMEOUT.get(host, port,
+ String.valueOf(options.dn())), e);
+ }
+ else if (e.getCause() instanceof IOException)
+ {
+ throw new DirectoryException(ResultCode.CLIENT_SIDE_SERVER_DOWN,
+ ERR_LDAP_PTA_CONNECTION_OTHER_ERROR.get(host, port,
+ String.valueOf(options.dn()), e.getMessage()), e);
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.CLIENT_SIDE_DECODING_ERROR,
+ ERR_LDAP_PTA_CONNECTION_DECODE_ERROR.get(host, port,
+ String.valueOf(options.dn()), e.getMessage()), e);
+ }
}
catch (final LDAPException e)
{
@@ -564,12 +582,22 @@
private final String host;
private final int port;
private final LDAPPassThroughAuthenticationPolicyCfg options;
-
private final int timeoutMS;
- private LDAPConnectionFactory(final String host, final int port,
+ /**
+ * LDAP connection factory implementation is package private so that it can
+ * be tested.
+ *
+ * @param host
+ * The server host name.
+ * @param port
+ * The server port.
+ * @param options
+ * The options (SSL).
+ */
+ LDAPConnectionFactory(final String host, final int port,
final LDAPPassThroughAuthenticationPolicyCfg options)
{
this.host = host;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
index 1d02578..28e3bad 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
@@ -29,15 +29,14 @@
import static org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.isFatalResultCode;
-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 static org.testng.Assert.*;
-import java.util.LinkedList;
-import java.util.Queue;
-import java.util.SortedSet;
-import java.util.TreeSet;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -49,6 +48,14 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.Connection;
import org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.ConnectionFactory;
+import org.opends.server.extensions.LDAPPassThroughAuthenticationPolicyFactory.LDAPConnectionFactory;
+import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.asn1.ASN1Writer;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.ProtocolOp;
+import org.opends.server.protocols.ldap.UnbindRequestProtocolOp;
+import org.opends.server.tools.LDAPReader;
+import org.opends.server.tools.LDAPWriter;
import org.opends.server.types.*;
import org.opends.server.util.StaticUtils;
import org.testng.annotations.BeforeClass;
@@ -78,7 +85,7 @@
- CloseEvent(GetConnectionEvent getConnectionEvent)
+ CloseEvent(final GetConnectionEvent getConnectionEvent)
{
this.getConnectionEvent = getConnectionEvent;
}
@@ -88,11 +95,12 @@
/**
* {@inheritDoc}
*/
- boolean matchesEvent(Event<?> event)
+ @Override
+ boolean matchesEvent(final Event<?> event)
{
if (event instanceof CloseEvent)
{
- CloseEvent closeEvent = (CloseEvent) event;
+ final CloseEvent closeEvent = (CloseEvent) event;
return getConnectionEvent.matchesEvent(closeEvent.getConnectionEvent);
}
else
@@ -106,7 +114,8 @@
/**
* {@inheritDoc}
*/
- StringBuilder toString(StringBuilder builder)
+ @Override
+ StringBuilder toString(final StringBuilder builder)
{
builder.append("CloseEvent(");
builder.append(getConnectionEvent);
@@ -121,7 +130,8 @@
static abstract class Event<T>
{
- public final boolean equals(Object obj)
+ @Override
+ public final boolean equals(final Object obj)
{
if (obj instanceof Event<?>)
{
@@ -135,9 +145,10 @@
+ @Override
public final String toString()
{
- StringBuilder builder = new StringBuilder();
+ final StringBuilder builder = new StringBuilder();
return toString(builder).toString();
}
@@ -166,15 +177,15 @@
- GetConnectionEvent(GetLDAPConnectionFactoryEvent fevent)
+ GetConnectionEvent(final GetLDAPConnectionFactoryEvent fevent)
{
this(fevent, ResultCode.SUCCESS);
}
- GetConnectionEvent(GetLDAPConnectionFactoryEvent fevent,
- ResultCode resultCode)
+ GetConnectionEvent(final GetLDAPConnectionFactoryEvent fevent,
+ final ResultCode resultCode)
{
this.fevent = fevent;
this.resultCode = resultCode;
@@ -185,6 +196,7 @@
/**
* {@inheritDoc}
*/
+ @Override
DirectoryException getResult()
{
if (resultCode != ResultCode.SUCCESS)
@@ -203,11 +215,12 @@
/**
* {@inheritDoc}
*/
- boolean matchesEvent(Event<?> event)
+ @Override
+ boolean matchesEvent(final Event<?> event)
{
if (event instanceof GetConnectionEvent)
{
- GetConnectionEvent getConnectionEvent = (GetConnectionEvent) event;
+ final GetConnectionEvent getConnectionEvent = (GetConnectionEvent) event;
return fevent.matchesEvent(getConnectionEvent.fevent);
}
else
@@ -221,7 +234,8 @@
/**
* {@inheritDoc}
*/
- StringBuilder toString(StringBuilder builder)
+ @Override
+ StringBuilder toString(final StringBuilder builder)
{
builder.append("GetConnectionEvent(");
builder.append(fevent);
@@ -240,8 +254,8 @@
- GetLDAPConnectionFactoryEvent(String hostPort,
- LDAPPassThroughAuthenticationPolicyCfg options)
+ GetLDAPConnectionFactoryEvent(final String hostPort,
+ final LDAPPassThroughAuthenticationPolicyCfg options)
{
this.hostPort = hostPort;
this.options = options;
@@ -252,11 +266,12 @@
/**
* {@inheritDoc}
*/
- boolean matchesEvent(Event<?> event)
+ @Override
+ boolean matchesEvent(final Event<?> event)
{
if (event instanceof GetLDAPConnectionFactoryEvent)
{
- GetLDAPConnectionFactoryEvent providerEvent = (GetLDAPConnectionFactoryEvent) event;
+ final GetLDAPConnectionFactoryEvent providerEvent = (GetLDAPConnectionFactoryEvent) event;
return hostPort.equals(providerEvent.hostPort)
&& options == providerEvent.options;
@@ -272,7 +287,8 @@
/**
* {@inheritDoc}
*/
- StringBuilder toString(StringBuilder builder)
+ @Override
+ StringBuilder toString(final StringBuilder builder)
{
builder.append("GetLDAPConnectionFactoryEvent(");
builder.append(hostPort);
@@ -295,8 +311,8 @@
- MockConnection(MockProvider mockProvider,
- GetConnectionEvent getConnectionEvent)
+ MockConnection(final MockProvider mockProvider,
+ final GetConnectionEvent getConnectionEvent)
{
this.mockProvider = mockProvider;
this.getConnectionEvent = getConnectionEvent;
@@ -307,9 +323,10 @@
/**
* {@inheritDoc}
*/
+ @Override
public void close()
{
- CloseEvent event = new CloseEvent(getConnectionEvent);
+ final CloseEvent event = new CloseEvent(getConnectionEvent);
mockProvider.assertExpectedEventWasReceived(event);
}
@@ -318,12 +335,13 @@
/**
* {@inheritDoc}
*/
- public ByteString search(DN baseDN, SearchScope scope, SearchFilter filter)
- throws DirectoryException
+ @Override
+ public ByteString search(final DN baseDN, final SearchScope scope,
+ final SearchFilter filter) throws DirectoryException
{
- SearchEvent event = new SearchEvent(getConnectionEvent,
+ final SearchEvent event = new SearchEvent(getConnectionEvent,
baseDN.toString(), scope, filter.toString());
- Object result = mockProvider.assertExpectedEventWasReceived(event);
+ final Object result = mockProvider.assertExpectedEventWasReceived(event);
if (result instanceof String)
{
return ByteString.valueOf((String) result);
@@ -339,13 +357,18 @@
/**
* {@inheritDoc}
*/
- public void simpleBind(ByteString username, ByteString password)
+ @Override
+ public void simpleBind(final ByteString username, final ByteString password)
throws DirectoryException
{
- SimpleBindEvent event = new SimpleBindEvent(getConnectionEvent,
+ final SimpleBindEvent event = new SimpleBindEvent(getConnectionEvent,
username.toString(), password.toString());
- DirectoryException e = mockProvider.assertExpectedEventWasReceived(event);
- if (e != null) throw e;
+ final DirectoryException e = mockProvider
+ .assertExpectedEventWasReceived(event);
+ if (e != null)
+ {
+ throw e;
+ }
}
}
@@ -360,8 +383,8 @@
- MockFactory(MockProvider mockProvider,
- GetLDAPConnectionFactoryEvent providerEvent)
+ MockFactory(final MockProvider mockProvider,
+ final GetLDAPConnectionFactoryEvent providerEvent)
{
this.mockProvider = mockProvider;
this.getLDAPConnectionFactoryEvent = providerEvent;
@@ -372,12 +395,14 @@
/**
* {@inheritDoc}
*/
+ @Override
public Connection getConnection() throws DirectoryException
{
- GetConnectionEvent event = new GetConnectionEvent(
+ final GetConnectionEvent event = new GetConnectionEvent(
getLDAPConnectionFactoryEvent);
- DirectoryException e = mockProvider.assertExpectedEventWasReceived(event);
+ final DirectoryException e = mockProvider
+ .assertExpectedEventWasReceived(event);
if (e != null)
{
throw e;
@@ -399,25 +424,29 @@
private MappingPolicy mappingPolicy = MappingPolicy.UNMAPPED;
private final SortedSet<String> primaryServers = new TreeSet<String>();
private final SortedSet<String> secondaryServers = new TreeSet<String>();
+ private int timeoutMS = 3000;
+ @Override
public void addChangeListener(
- ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
+ final ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
{
// Do nothing.
}
+ @Override
public void addLDAPPassThroughChangeListener(
- ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
+ final ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
{
// Do nothing.
}
+ @Override
public Class<? extends LDAPPassThroughAuthenticationPolicyCfg> configurationClass()
{
return LDAPPassThroughAuthenticationPolicyCfg.class;
@@ -425,6 +454,7 @@
+ @Override
public DN dn()
{
return policyDN;
@@ -432,13 +462,15 @@
+ @Override
public long getConnectionTimeout()
{
- return 3000;
+ return timeoutMS;
}
+ @Override
public String getJavaClass()
{
return LDAPPassThroughAuthenticationPolicyFactory.class.getName();
@@ -446,6 +478,7 @@
+ @Override
public SortedSet<AttributeType> getMappedAttribute()
{
return mappedAttributes;
@@ -453,6 +486,7 @@
+ @Override
public SortedSet<DN> getMappedSearchBaseDN()
{
return baseDNs;
@@ -460,6 +494,7 @@
+ @Override
public DN getMappedSearchBindDN()
{
return searchBindDN;
@@ -467,6 +502,7 @@
+ @Override
public String getMappedSearchBindPassword()
{
return "searchPassword";
@@ -474,6 +510,7 @@
+ @Override
public MappingPolicy getMappingPolicy()
{
return mappingPolicy;
@@ -481,6 +518,7 @@
+ @Override
public SortedSet<String> getPrimaryRemoteLDAPServer()
{
return primaryServers;
@@ -488,6 +526,7 @@
+ @Override
public SortedSet<String> getSecondaryRemoteLDAPServer()
{
return secondaryServers;
@@ -495,6 +534,7 @@
+ @Override
public SortedSet<String> getSSLCipherSuite()
{
return new TreeSet<String>();
@@ -502,6 +542,7 @@
+ @Override
public SortedSet<String> getSSLProtocol()
{
return new TreeSet<String>();
@@ -509,6 +550,7 @@
+ @Override
public String getTrustManagerProvider()
{
return "ignored";
@@ -516,6 +558,7 @@
+ @Override
public DN getTrustManagerProviderDN()
{
return trustManagerDN;
@@ -523,6 +566,7 @@
+ @Override
public boolean isUseSSL()
{
return false;
@@ -530,6 +574,7 @@
+ @Override
public boolean isUseTCPKeepAlive()
{
return false;
@@ -537,6 +582,7 @@
+ @Override
public boolean isUseTCPNoDelay()
{
return false;
@@ -544,29 +590,31 @@
+ @Override
public void removeChangeListener(
- ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
+ final ConfigurationChangeListener<AuthenticationPolicyCfg> listener)
{
// Do nothing.
}
+ @Override
public void removeLDAPPassThroughChangeListener(
- ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
+ final ConfigurationChangeListener<LDAPPassThroughAuthenticationPolicyCfg> listener)
{
// Do nothing.
}
- MockPolicyCfg withBaseDN(String baseDN)
+ MockPolicyCfg withBaseDN(final String baseDN)
{
try
{
baseDNs.add(DN.decode(baseDN));
}
- catch (DirectoryException e)
+ catch (final DirectoryException e)
{
throw new RuntimeException(e);
}
@@ -575,7 +623,15 @@
- MockPolicyCfg withMappedAttribute(String atype)
+ MockPolicyCfg withConnectionTimeout(final int timeoutMS)
+ {
+ this.timeoutMS = timeoutMS;
+ return this;
+ }
+
+
+
+ MockPolicyCfg withMappedAttribute(final String atype)
{
mappedAttributes.add(DirectoryServer.getAttributeType(
StaticUtils.toLowerCase(atype), true));
@@ -584,7 +640,7 @@
- MockPolicyCfg withMappingPolicy(MappingPolicy policy)
+ MockPolicyCfg withMappingPolicy(final MappingPolicy policy)
{
this.mappingPolicy = policy;
return this;
@@ -592,7 +648,7 @@
- MockPolicyCfg withPrimaryServer(String hostPort)
+ MockPolicyCfg withPrimaryServer(final String hostPort)
{
primaryServers.add(hostPort);
return this;
@@ -600,7 +656,7 @@
- MockPolicyCfg withSecondaryServer(String hostPort)
+ MockPolicyCfg withSecondaryServer(final String hostPort)
{
secondaryServers.add(hostPort);
return this;
@@ -620,10 +676,11 @@
/**
* {@inheritDoc}
*/
- public ConnectionFactory getLDAPConnectionFactory(String host, int port,
- LDAPPassThroughAuthenticationPolicyCfg options)
+ @Override
+ public ConnectionFactory getLDAPConnectionFactory(final String host,
+ final int port, final LDAPPassThroughAuthenticationPolicyCfg options)
{
- GetLDAPConnectionFactoryEvent event = new GetLDAPConnectionFactoryEvent(
+ final GetLDAPConnectionFactoryEvent event = new GetLDAPConnectionFactoryEvent(
host + ":" + port, options);
assertExpectedEventWasReceived(event);
return new MockFactory(this, event);
@@ -631,10 +688,17 @@
- @SuppressWarnings("unchecked")
- <T> T assertExpectedEventWasReceived(Event<T> actualEvent)
+ void assertAllExpectedEventsReceived()
{
- Event<?> expectedEvent = expectedEvents.poll();
+ assertTrue(expectedEvents.isEmpty());
+ }
+
+
+
+ @SuppressWarnings("unchecked")
+ <T> T assertExpectedEventWasReceived(final Event<T> actualEvent)
+ {
+ final Event<?> expectedEvent = expectedEvents.poll();
if (expectedEvent == null)
{
fail("Unexpected event: " + actualEvent);
@@ -648,18 +712,302 @@
- MockProvider expectEvent(Event<?> expectedEvent)
+ MockProvider expectEvent(final Event<?> expectedEvent)
{
expectedEvents.add(expectedEvent);
return this;
}
+ }
+
+
+
+ final class MockServer
+ {
+ // Waits for an incoming client connection.
+ class AcceptAction extends Action
+ {
+ void run() throws Exception
+ {
+ accept();
+ }
+ }
+
+
+
+ abstract class Action
+ {
+ abstract void run() throws Exception;
+ }
+
+
+
+ // Blocks the server until it is released.
+ class BlockAction extends Action
+ {
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+
+
+ void release()
+ {
+ latch.countDown();
+ }
+
+
+
+ void run() throws Exception
+ {
+ latch.await();
+ }
+ }
+
+
+
+ // Close the client socket.
+ class CloseAction extends Action
+ {
+ @Override
+ void run() throws Exception
+ {
+ getSocket().close();
+ }
+ }
+
+
+
+ // Read the next message and check it matches the expected message.
+ class ReceiveAction extends Action
+ {
+ private final int messageID;
+ private final ProtocolOp expectedOp;
+
+
+
+ ReceiveAction(final int messageID, final ProtocolOp expectedOp)
+ {
+ this.messageID = messageID;
+ this.expectedOp = expectedOp;
+ }
+
+
+
+ @Override
+ void run() throws Exception
+ {
+ // Read next message.
+ final LDAPReader reader = new LDAPReader(getSocket());
+ final LDAPMessage message = reader.readMessage();
+
+ // Check message ID matches.
+ assertEquals(message.getMessageID(), messageID);
+
+ // Check protocol op matches.
+ final ProtocolOp actualOp = message.getProtocolOp();
+ final ByteStringBuilder b1 = new ByteStringBuilder();
+ final ByteStringBuilder b2 = new ByteStringBuilder();
+ final ASN1Writer w1 = ASN1.getWriter(b1);
+ final ASN1Writer w2 = ASN1.getWriter(b2);
+ expectedOp.write(w1);
+ actualOp.write(w2);
+ assertEquals(b1, b2);
+ }
+ }
+
+
+
+ // Sends a message.
+ class SendAction extends Action
+ {
+ private final int messageID;
+ private final ProtocolOp op;
+
+
+
+ SendAction(final int messageID, final ProtocolOp op)
+ {
+ this.messageID = messageID;
+ this.op = op;
+ }
+
+
+
+ @Override
+ void run() throws Exception
+ {
+ final LDAPWriter writer = new LDAPWriter(getSocket());
+ final LDAPMessage message = new LDAPMessage(messageID, op);
+ writer.writeMessage(message);
+ }
+ }
+
+
+
+ private final ServerSocket serverSocket;
+ private final List<Action> actions = new LinkedList<Action>();
+ private Socket socket = null;
+ private volatile Exception e = null;
+ private Thread serverThread = null;
+ private final CountDownLatch stopLatch = new CountDownLatch(1);
+ private final Queue<BlockAction> blockers = new LinkedList<BlockAction>();
+
+
+
+ MockServer(final ServerSocket serverSocket)
+ {
+ this.serverSocket = serverSocket;
+ }
+
+
+
+ MockServer thenAccept()
+ {
+ actions.add(new AcceptAction());
+ return this;
+ }
- void assertAllExpectedEventsReceived()
+ void assertNoExceptions() throws Exception
{
- assertTrue(expectedEvents.isEmpty());
+ if (e != null)
+ {
+ throw e;
+ }
}
+
+
+
+ int getPort()
+ {
+ return serverSocket.getLocalPort();
+ }
+
+
+
+ MockServer start()
+ {
+ serverThread = new Thread(new Runnable()
+ {
+ /**
+ * {@inheritDoc}
+ */
+ public void run()
+ {
+ for (final Action action : actions)
+ {
+ try
+ {
+ action.run();
+ }
+ catch (final Exception e)
+ {
+ MockServer.this.e = e;
+ break;
+ }
+ }
+
+ if (socket != null)
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (final IOException ignored)
+ {
+ // Ignore
+ }
+ }
+ if (serverSocket != null)
+ {
+ try
+ {
+ serverSocket.close();
+ }
+ catch (final IOException ignored)
+ {
+ // Ignore
+ }
+ }
+
+ // Release test thread.
+ stopLatch.countDown();
+ }
+ }, "MockServer on port " + serverSocket.getLocalPort());
+ serverThread.start();
+ return this;
+ }
+
+
+
+ void stop() throws Exception
+ {
+ stopLatch.await(10, TimeUnit.SECONDS);
+ if (serverThread != null)
+ {
+ serverThread.interrupt();
+ }
+ stopLatch.await();
+ assertNoExceptions();
+ }
+
+
+
+ MockServer thenBlock()
+ {
+ BlockAction action = new BlockAction();
+ actions.add(action);
+ blockers.add(action);
+ return this;
+ }
+
+
+
+ MockServer thenClose()
+ {
+ actions.add(new CloseAction());
+ return this;
+ }
+
+
+
+ MockServer thenReceive(final int messageID, final ProtocolOp op)
+ {
+ actions.add(new ReceiveAction(messageID, op));
+ return this;
+ }
+
+
+
+ MockServer thenSend(final int messageID, final ProtocolOp op)
+ {
+ actions.add(new SendAction(messageID, op));
+ return this;
+ }
+
+
+
+ void unblock() throws Exception
+ {
+ BlockAction action = blockers.poll();
+ assertNotNull(action);
+ action.release();
+ }
+
+
+
+ private Socket accept() throws IOException
+ {
+ socket = serverSocket.accept();
+ return socket;
+ }
+
+
+
+ private Socket getSocket()
+ {
+ return socket;
+ }
+
}
@@ -675,32 +1023,34 @@
- SearchEvent(GetConnectionEvent cevent, String baseDN, SearchScope scope,
- String filter)
+ SearchEvent(final GetConnectionEvent cevent, final String baseDN,
+ final SearchScope scope, final String filter)
{
this(cevent, baseDN, scope, filter, null, ResultCode.SUCCESS);
}
- SearchEvent(GetConnectionEvent cevent, String baseDN, SearchScope scope,
- String filter, String username)
- {
- this(cevent, baseDN, scope, filter, username, ResultCode.SUCCESS);
- }
-
-
-
- SearchEvent(GetConnectionEvent cevent, String baseDN, SearchScope scope,
- String filter, ResultCode resultCode)
+ SearchEvent(final GetConnectionEvent cevent, final String baseDN,
+ final SearchScope scope, final String filter,
+ final ResultCode resultCode)
{
this(cevent, baseDN, scope, filter, null, resultCode);
}
- private SearchEvent(GetConnectionEvent cevent, String baseDN,
- SearchScope scope, String filter, String username, ResultCode resultCode)
+ SearchEvent(final GetConnectionEvent cevent, final String baseDN,
+ final SearchScope scope, final String filter, final String username)
+ {
+ this(cevent, baseDN, scope, filter, username, ResultCode.SUCCESS);
+ }
+
+
+
+ private SearchEvent(final GetConnectionEvent cevent, final String baseDN,
+ final SearchScope scope, final String filter, final String username,
+ final ResultCode resultCode)
{
this.cevent = cevent;
this.baseDN = baseDN;
@@ -715,6 +1065,7 @@
/**
* {@inheritDoc}
*/
+ @Override
Object getResult()
{
return resultCode == ResultCode.SUCCESS ? username
@@ -726,11 +1077,12 @@
/**
* {@inheritDoc}
*/
- boolean matchesEvent(Event<?> event)
+ @Override
+ boolean matchesEvent(final Event<?> event)
{
if (event instanceof SearchEvent)
{
- SearchEvent searchEvent = (SearchEvent) event;
+ final SearchEvent searchEvent = (SearchEvent) event;
return cevent.matchesEvent(searchEvent.cevent)
&& baseDN.equals(searchEvent.baseDN)
&& scope.equals(searchEvent.scope)
@@ -747,7 +1099,8 @@
/**
* {@inheritDoc}
*/
- StringBuilder toString(StringBuilder builder)
+ @Override
+ StringBuilder toString(final StringBuilder builder)
{
builder.append("SearchEvent(");
builder.append(cevent);
@@ -774,15 +1127,16 @@
- SimpleBindEvent(GetConnectionEvent cevent, String username, String password)
+ SimpleBindEvent(final GetConnectionEvent cevent, final String username,
+ final String password)
{
this(cevent, username, password, ResultCode.SUCCESS);
}
- SimpleBindEvent(GetConnectionEvent cevent, String username,
- String password, ResultCode resultCode)
+ SimpleBindEvent(final GetConnectionEvent cevent, final String username,
+ final String password, final ResultCode resultCode)
{
this.cevent = cevent;
this.username = username;
@@ -795,6 +1149,7 @@
/**
* {@inheritDoc}
*/
+ @Override
DirectoryException getResult()
{
if (resultCode != ResultCode.SUCCESS)
@@ -813,11 +1168,12 @@
/**
* {@inheritDoc}
*/
- boolean matchesEvent(Event<?> event)
+ @Override
+ boolean matchesEvent(final Event<?> event)
{
if (event instanceof SimpleBindEvent)
{
- SimpleBindEvent simpleBindEvent = (SimpleBindEvent) event;
+ final SimpleBindEvent simpleBindEvent = (SimpleBindEvent) event;
return cevent.matchesEvent(simpleBindEvent.cevent)
&& username.equals(simpleBindEvent.username)
&& password.equals(simpleBindEvent.password);
@@ -833,7 +1189,8 @@
/**
* {@inheritDoc}
*/
- StringBuilder toString(StringBuilder builder)
+ @Override
+ StringBuilder toString(final StringBuilder builder)
{
builder.append("SimpleBindEvent(");
builder.append(cevent);
@@ -854,20 +1211,21 @@
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 policy,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 trustManagerDNString = "cn=ignored";
private final String adDNString = "uid=aduser,o=ad";
private final String opendjDNString = "cn=test user,o=opendj";
private Entry userEntry;
+
private final String userPassword = "password";
@@ -903,56 +1261,52 @@
/**
- * Returns test data for
- * {@link #testConnectionFailureDuringSearchGetConnection}.
+ * Tests that failures during the search are handled properly.
+ * <p>
+ * Non-fatal errors (e.g. entry not found, too many entries returned) should
+ * not cause the search connection to be closed.
*
- * @return Test data for
- * {@link #testConnectionFailureDuringSearchGetConnection}.
- */
- @DataProvider
- public Object[][] testConnectionFailureDuringSearchGetConnectionData()
- {
- // @formatter:off
- return new Object[][] {
- { ResultCode.UNAVAILABLE }
- };
- // @formatter:on
- }
-
-
-
- /**
- * Tests that failures to obtain a search connection are handled properly.
- *
- * @param connectResultCode
- * The connection failure result code.
+ * @param searchResultCode
+ * The search result code.
* @throws Exception
* If an unexpected exception occurred.
*/
- @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchGetConnectionData")
- public void testConnectionFailureDuringSearchGetConnection(
- ResultCode connectResultCode) throws Exception
+ @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchData")
+ public void testConnectionFailureDuringSearch(
+ final ResultCode searchResultCode) throws Exception
{
// Mock configuration.
- LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
.withPrimaryServer(phost1)
.withMappingPolicy(MappingPolicy.MAPPED_SEARCH)
.withMappedAttribute("uid").withBaseDN("o=ad");
// Create the provider and its list of expected events.
- GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
+ final GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
phost1, cfg);
- GetConnectionEvent ceSearch = new GetConnectionEvent(fe, connectResultCode);
+ final GetConnectionEvent ceSearch = new GetConnectionEvent(fe);
- MockProvider provider = new MockProvider().expectEvent(fe).expectEvent(
- ceSearch);
+ final MockProvider provider = new MockProvider()
+ .expectEvent(fe)
+ .expectEvent(ceSearch)
+ .expectEvent(
+ new SimpleBindEvent(ceSearch, searchBindDNString, "searchPassword",
+ ResultCode.SUCCESS))
+ .expectEvent(
+ new SearchEvent(ceSearch, "o=ad", SearchScope.WHOLE_SUBTREE,
+ "(uid=aduser)", searchResultCode));
+ if (isFatalResultCode(searchResultCode))
+ {
+ // The connection will fail and be closed immediately.
+ provider.expectEvent(new CloseEvent(ceSearch));
+ }
// Obtain policy and state.
- LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
+ final LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
provider);
assertTrue(factory.isConfigurationAcceptable(cfg, null));
- AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
- AuthenticationPolicyState state = policy
+ final AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
+ final AuthenticationPolicyState state = policy
.createAuthenticationPolicyState(userEntry);
assertEquals(state.getAuthenticationPolicy(), policy);
@@ -962,7 +1316,81 @@
state.passwordMatches(ByteString.valueOf(userPassword));
fail("password match unexpectedly succeeded");
}
- catch (DirectoryException e)
+ catch (final DirectoryException e)
+ {
+ // No valid connections available so this should always fail with
+ // INVALID_CREDENTIALS.
+ assertEquals(e.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+ }
+
+ // There should be no more pending events.
+ provider.assertAllExpectedEventsReceived();
+ state.finalizeStateAfterBind();
+
+ // Cached connections should be closed when the policy is finalized.
+ if (!isFatalResultCode(searchResultCode))
+ {
+ provider.expectEvent(new CloseEvent(ceSearch));
+ }
+
+ // Tear down and check final state.
+ policy.finalizeAuthenticationPolicy();
+ provider.assertAllExpectedEventsReceived();
+ }
+
+
+
+ /**
+ * Tests that failures to authenticate a search connection are handled
+ * properly.
+ * <p>
+ * Any kind of failure occurring while trying to authenticate a search
+ * connection should result in the connection being closed and periodically
+ * retried.
+ *
+ * @param bindResultCode
+ * The bind result code.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchBindData")
+ public void testConnectionFailureDuringSearchBind(
+ final ResultCode bindResultCode) throws Exception
+ {
+ // Mock configuration.
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
+ .withPrimaryServer(phost1)
+ .withMappingPolicy(MappingPolicy.MAPPED_SEARCH)
+ .withMappedAttribute("uid").withBaseDN("o=ad");
+
+ // Create the provider and its list of expected events.
+ final GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
+ phost1, cfg);
+ final GetConnectionEvent ceSearch = new GetConnectionEvent(fe);
+
+ final MockProvider provider = new MockProvider()
+ .expectEvent(fe)
+ .expectEvent(ceSearch)
+ .expectEvent(
+ new SimpleBindEvent(ceSearch, searchBindDNString, "searchPassword",
+ bindResultCode)).expectEvent(new CloseEvent(ceSearch));
+
+ // Obtain policy and state.
+ final LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
+ provider);
+ assertTrue(factory.isConfigurationAcceptable(cfg, null));
+ final AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
+ final AuthenticationPolicyState state = policy
+ .createAuthenticationPolicyState(userEntry);
+ assertEquals(state.getAuthenticationPolicy(), policy);
+
+ // Perform authentication.
+ try
+ {
+ state.passwordMatches(ByteString.valueOf(userPassword));
+ fail("password match unexpectedly succeeded");
+ }
+ catch (final DirectoryException e)
{
// No valid connections available so this should always fail with
// INVALID_CREDENTIALS.
@@ -997,71 +1425,6 @@
/**
- * Tests that failures to authenticate a search connection are handled
- * properly.
- * <p>
- * Any kind of failure occurring while trying to authenticate a search
- * connection should result in the connection being closed and periodically
- * retried.
- *
- * @param bindResultCode
- * The bind result code.
- * @throws Exception
- * If an unexpected exception occurred.
- */
- @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchBindData")
- public void testConnectionFailureDuringSearchBind(ResultCode bindResultCode)
- throws Exception
- {
- // Mock configuration.
- LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
- .withPrimaryServer(phost1)
- .withMappingPolicy(MappingPolicy.MAPPED_SEARCH)
- .withMappedAttribute("uid").withBaseDN("o=ad");
-
- // Create the provider and its list of expected events.
- GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
- phost1, cfg);
- GetConnectionEvent ceSearch = new GetConnectionEvent(fe);
-
- MockProvider provider = new MockProvider()
- .expectEvent(fe)
- .expectEvent(ceSearch)
- .expectEvent(
- new SimpleBindEvent(ceSearch, searchBindDNString, "searchPassword",
- bindResultCode)).expectEvent(new CloseEvent(ceSearch));
-
- // Obtain policy and state.
- LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
- provider);
- assertTrue(factory.isConfigurationAcceptable(cfg, null));
- AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
- AuthenticationPolicyState state = policy
- .createAuthenticationPolicyState(userEntry);
- assertEquals(state.getAuthenticationPolicy(), policy);
-
- // Perform authentication.
- try
- {
- state.passwordMatches(ByteString.valueOf(userPassword));
- fail("password match unexpectedly succeeded");
- }
- catch (DirectoryException e)
- {
- // No valid connections available so this should always fail with
- // INVALID_CREDENTIALS.
- assertEquals(e.getResultCode(), ResultCode.INVALID_CREDENTIALS);
- }
-
- // Tear down and check final state.
- provider.assertAllExpectedEventsReceived();
- state.finalizeStateAfterBind();
- policy.finalizeAuthenticationPolicy();
- }
-
-
-
- /**
* Returns test data for {@link #testConnectionFailureDuringSearch}.
*
* @return Test data for {@link #testConnectionFailureDuringSearch}.
@@ -1082,52 +1445,38 @@
/**
- * Tests that failures during the search are handled properly.
- * <p>
- * Non-fatal errors (e.g. entry not found, too many entries returned) should
- * not cause the search connection to be closed.
+ * Tests that failures to obtain a search connection are handled properly.
*
- * @param searchResultCode
- * The search result code.
+ * @param connectResultCode
+ * The connection failure result code.
* @throws Exception
* If an unexpected exception occurred.
*/
- @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchData")
- public void testConnectionFailureDuringSearch(ResultCode searchResultCode)
- throws Exception
+ @Test(enabled = true, dataProvider = "testConnectionFailureDuringSearchGetConnectionData")
+ public void testConnectionFailureDuringSearchGetConnection(
+ final ResultCode connectResultCode) throws Exception
{
// Mock configuration.
- LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
.withPrimaryServer(phost1)
.withMappingPolicy(MappingPolicy.MAPPED_SEARCH)
.withMappedAttribute("uid").withBaseDN("o=ad");
// Create the provider and its list of expected events.
- GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
+ final GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
phost1, cfg);
- GetConnectionEvent ceSearch = new GetConnectionEvent(fe);
+ final GetConnectionEvent ceSearch = new GetConnectionEvent(fe,
+ connectResultCode);
- MockProvider provider = new MockProvider()
- .expectEvent(fe)
- .expectEvent(ceSearch)
- .expectEvent(
- new SimpleBindEvent(ceSearch, searchBindDNString, "searchPassword",
- ResultCode.SUCCESS))
- .expectEvent(
- new SearchEvent(ceSearch, "o=ad", SearchScope.WHOLE_SUBTREE,
- "(uid=aduser)", searchResultCode));
- if (isFatalResultCode(searchResultCode))
- {
- // The connection will fail and be closed immediately.
- provider.expectEvent(new CloseEvent(ceSearch));
- }
+ final MockProvider provider = new MockProvider().expectEvent(fe)
+ .expectEvent(ceSearch);
// Obtain policy and state.
- LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
+ final LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
provider);
assertTrue(factory.isConfigurationAcceptable(cfg, null));
- AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
- AuthenticationPolicyState state = policy
+ final AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
+ final AuthenticationPolicyState state = policy
.createAuthenticationPolicyState(userEntry);
assertEquals(state.getAuthenticationPolicy(), policy);
@@ -1137,22 +1486,229 @@
state.passwordMatches(ByteString.valueOf(userPassword));
fail("password match unexpectedly succeeded");
}
- catch (DirectoryException e)
+ catch (final DirectoryException e)
{
// No valid connections available so this should always fail with
// INVALID_CREDENTIALS.
assertEquals(e.getResultCode(), ResultCode.INVALID_CREDENTIALS);
}
+ // Tear down and check final state.
+ provider.assertAllExpectedEventsReceived();
+ state.finalizeStateAfterBind();
+ policy.finalizeAuthenticationPolicy();
+ }
+
+
+
+ /**
+ * Returns test data for
+ * {@link #testConnectionFailureDuringSearchGetConnection}.
+ *
+ * @return Test data for
+ * {@link #testConnectionFailureDuringSearchGetConnection}.
+ */
+ @DataProvider
+ public Object[][] testConnectionFailureDuringSearchGetConnectionData()
+ {
+ // @formatter:off
+ return new Object[][] {
+ { ResultCode.UNAVAILABLE }
+ };
+ // @formatter:on
+ }
+
+
+
+ /**
+ * Checks connect and immediately unbind work ok.
+ *
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(enabled = true)
+ public void testLDAPConnectionFactoryConnectAndUnbind() throws Exception
+ {
+ // Mock configuration.
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg();
+
+ // Mock server.
+ final MockServer server = mockServer(cfg).thenAccept()
+ .thenReceive(1, new UnbindRequestProtocolOp()).thenClose().start();
+
+ // Test connect and close.
+ final LDAPConnectionFactory factory = new LDAPConnectionFactory(
+ "127.0.0.1", server.getPort(), cfg);
+ try
+ {
+ Connection connection = factory.getConnection();
+ connection.close();
+ }
+ finally
+ {
+ server.stop();
+ }
+ }
+
+
+
+ /**
+ * Checks that client side IO timeouts are enforced.
+ *
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(enabled = true)
+ public void testLDAPConnectionFactoryConnectTimeout() throws Exception
+ {
+ // Mock configuration.
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
+ .withConnectionTimeout(500);
+
+ // Mock server.
+ final MockServer server = mockServer(cfg).thenAccept().thenBlock().start();
+
+ // Test connect and close.
+ final LDAPConnectionFactory factory = new LDAPConnectionFactory(
+ "127.0.0.1", server.getPort(), cfg);
+ Connection connection = null;
+ try
+ {
+ connection = factory.getConnection();
+ connection.search(searchBindDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ fail("Search attempt should have timed out");
+ }
+ catch (DirectoryException e)
+ {
+ if (e.getResultCode() != ResultCode.CLIENT_SIDE_TIMEOUT)
+ {
+ throw e;
+ }
+ }
+ finally
+ {
+ if (connection != null)
+ {
+ connection.close();
+ }
+ server.unblock();
+ server.stop();
+ }
+ }
+
+
+
+ /**
+ * Tests the different mapping policies: connection attempts will succeed, as
+ * will any searches, but the final user bind may or may not succeed depending
+ * on the provided result code.
+ * <p>
+ * Non-fatal errors (e.g. entry not found) should not cause the bind
+ * connection to be closed.
+ *
+ * @param mappingPolicy
+ * The mapping policy.
+ * @param bindResultCode
+ * The bind result code.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(enabled = true, dataProvider = "testMappingPolicyAuthenticationData")
+ public void testMappingPolicyAuthentication(
+ final MappingPolicy mappingPolicy, final ResultCode bindResultCode)
+ throws Exception
+ {
+ // Mock configuration.
+ final LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
+ .withPrimaryServer(phost1)
+ .withMappingPolicy(mappingPolicy)
+ .withMappedAttribute(
+ mappingPolicy == MappingPolicy.MAPPED_BIND ? "aduser" : "uid")
+ .withBaseDN("o=ad");
+
+ // Create the provider and its list of expected events.
+ final GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
+ phost1, cfg);
+ final MockProvider provider = new MockProvider().expectEvent(fe);
+
+ // Add search events if doing a mapped search.
+ GetConnectionEvent ceSearch = null;
+ if (mappingPolicy == MappingPolicy.MAPPED_SEARCH)
+ {
+ ceSearch = new GetConnectionEvent(fe);
+ provider
+ .expectEvent(ceSearch)
+ .expectEvent(
+ new SimpleBindEvent(ceSearch, searchBindDNString,
+ "searchPassword", ResultCode.SUCCESS))
+ .expectEvent(
+ new SearchEvent(ceSearch, "o=ad", SearchScope.WHOLE_SUBTREE,
+ "(uid=aduser)", adDNString));
+ // Connection should be cached until the policy is finalized.
+ }
+
+ // Add bind events.
+ final GetConnectionEvent ceBind = new GetConnectionEvent(fe);
+ provider.expectEvent(ceBind).expectEvent(
+ new SimpleBindEvent(ceBind,
+ mappingPolicy == MappingPolicy.UNMAPPED ? opendjDNString
+ : adDNString, userPassword, bindResultCode));
+ if (isFatalResultCode(bindResultCode))
+ {
+ // The connection will fail and be closed immediately.
+ provider.expectEvent(new CloseEvent(ceBind));
+ }
+
+ // Connection should be cached until the policy is finalized or until the
+ // connection fails.
+
+ // Obtain policy and state.
+ final LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
+ provider);
+ assertTrue(factory.isConfigurationAcceptable(cfg, null));
+ final AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
+ final AuthenticationPolicyState state = policy
+ .createAuthenticationPolicyState(userEntry);
+ assertEquals(state.getAuthenticationPolicy(), policy);
+
+ // Perform authentication.
+ switch (bindResultCode)
+ {
+ case SUCCESS:
+ assertTrue(state.passwordMatches(ByteString.valueOf(userPassword)));
+ break;
+ case INVALID_CREDENTIALS:
+ assertFalse(state.passwordMatches(ByteString.valueOf(userPassword)));
+ break;
+ default:
+ try
+ {
+ state.passwordMatches(ByteString.valueOf(userPassword));
+ fail("password match did not fail");
+ }
+ catch (final DirectoryException e)
+ {
+ // No valid connections available so this should always fail with
+ // INVALID_CREDENTIALS.
+ assertEquals(e.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+ }
+ break;
+ }
+
// There should be no more pending events.
provider.assertAllExpectedEventsReceived();
state.finalizeStateAfterBind();
// Cached connections should be closed when the policy is finalized.
- if (!isFatalResultCode(searchResultCode))
+ if (ceSearch != null)
{
provider.expectEvent(new CloseEvent(ceSearch));
}
+ if (!isFatalResultCode(bindResultCode))
+ {
+ provider.expectEvent(new CloseEvent(ceBind));
+ }
// Tear down and check final state.
policy.finalizeAuthenticationPolicy();
@@ -1189,125 +1745,17 @@
- /**
- * Tests the different mapping policies: connection attempts will succeed, as
- * will any searches, but the final user bind may or may not succeed depending
- * on the provided result code.
- * <p>
- * Non-fatal errors (e.g. entry not found) should not cause the bind
- * connection to be closed.
- *
- * @param mappingPolicy
- * The mapping policy.
- * @param bindResultCode
- * The bind result code.
- * @throws Exception
- * If an unexpected exception occurred.
- */
- @Test(enabled = true, dataProvider = "testMappingPolicyAuthenticationData")
- public void testMappingPolicyAuthentication(MappingPolicy mappingPolicy,
- ResultCode bindResultCode) throws Exception
+ MockPolicyCfg mockCfg()
{
- // Mock configuration.
- LDAPPassThroughAuthenticationPolicyCfg cfg = mockCfg()
- .withPrimaryServer(phost1)
- .withMappingPolicy(mappingPolicy)
- .withMappedAttribute(
- mappingPolicy == MappingPolicy.MAPPED_BIND ? "aduser" : "uid")
- .withBaseDN("o=ad");
-
- // Create the provider and its list of expected events.
- GetLDAPConnectionFactoryEvent fe = new GetLDAPConnectionFactoryEvent(
- phost1, cfg);
- MockProvider provider = new MockProvider().expectEvent(fe);
-
- // Add search events if doing a mapped search.
- GetConnectionEvent ceSearch = null;
- if (mappingPolicy == MappingPolicy.MAPPED_SEARCH)
- {
- ceSearch = new GetConnectionEvent(fe);
- provider
- .expectEvent(ceSearch)
- .expectEvent(
- new SimpleBindEvent(ceSearch, searchBindDNString,
- "searchPassword", ResultCode.SUCCESS))
- .expectEvent(
- new SearchEvent(ceSearch, "o=ad", SearchScope.WHOLE_SUBTREE,
- "(uid=aduser)", adDNString));
- // Connection should be cached until the policy is finalized.
- }
-
- // Add bind events.
- GetConnectionEvent ceBind = new GetConnectionEvent(fe);
- provider.expectEvent(ceBind).expectEvent(
- new SimpleBindEvent(ceBind,
- mappingPolicy == MappingPolicy.UNMAPPED ? opendjDNString
- : adDNString, userPassword, bindResultCode));
- if (isFatalResultCode(bindResultCode))
- {
- // The connection will fail and be closed immediately.
- provider.expectEvent(new CloseEvent(ceBind));
- }
-
- // Connection should be cached until the policy is finalized or until the
- // connection fails.
-
- // Obtain policy and state.
- LDAPPassThroughAuthenticationPolicyFactory factory = new LDAPPassThroughAuthenticationPolicyFactory(
- provider);
- assertTrue(factory.isConfigurationAcceptable(cfg, null));
- AuthenticationPolicy policy = factory.createAuthenticationPolicy(cfg);
- AuthenticationPolicyState state = policy
- .createAuthenticationPolicyState(userEntry);
- assertEquals(state.getAuthenticationPolicy(), policy);
-
- // Perform authentication.
- switch (bindResultCode)
- {
- case SUCCESS:
- assertTrue(state.passwordMatches(ByteString.valueOf(userPassword)));
- break;
- case INVALID_CREDENTIALS:
- assertFalse(state.passwordMatches(ByteString.valueOf(userPassword)));
- break;
- default:
- try
- {
- state.passwordMatches(ByteString.valueOf(userPassword));
- fail("password match did not fail");
- }
- catch (DirectoryException e)
- {
- // No valid connections available so this should always fail with
- // INVALID_CREDENTIALS.
- assertEquals(e.getResultCode(), ResultCode.INVALID_CREDENTIALS);
- }
- break;
- }
-
- // There should be no more pending events.
- provider.assertAllExpectedEventsReceived();
- state.finalizeStateAfterBind();
-
- // Cached connections should be closed when the policy is finalized.
- if (ceSearch != null)
- {
- provider.expectEvent(new CloseEvent(ceSearch));
- }
- if (!isFatalResultCode(bindResultCode))
- {
- provider.expectEvent(new CloseEvent(ceBind));
- }
-
- // Tear down and check final state.
- policy.finalizeAuthenticationPolicy();
- provider.assertAllExpectedEventsReceived();
+ return new MockPolicyCfg();
}
- private MockPolicyCfg mockCfg()
+ MockServer mockServer(final LDAPPassThroughAuthenticationPolicyCfg cfg)
+ throws IOException
{
- return new MockPolicyCfg();
+ final ServerSocket serverSocket = TestCaseUtils.bindFreePort();
+ return new MockServer(serverSocket);
}
}
--
Gitblit v1.10.0