test: ldap timeout test failing on windows

This extends the connect timeout on windows to give it enought time to complete.  It moves the ldap read timeout test to openldap and active directory.

We now have three timeouts configurable.  The timeout tests on active directory only work for TCP connect, and TCP read, but not LDAP Search.

Original commit: elastic/x-pack-elasticsearch@ff97396f60
This commit is contained in:
c-a-m 2014-12-15 17:28:16 -07:00
parent 38a0ec9c3e
commit 661b755639
8 changed files with 90 additions and 39 deletions

View File

@ -24,13 +24,15 @@ public class ActiveDirectoryConnection extends AbstractLdapConnection {
private static final ESLogger logger = Loggers.getLogger(ActiveDirectoryConnection.class);
private final String groupSearchDN;
private final int timeoutMilliseconds;
/**
* This object is intended to be constructed by the LdapConnectionFactory
*/
ActiveDirectoryConnection(DirContext ctx, String boundName, String groupSearchDN) {
ActiveDirectoryConnection(DirContext ctx, String boundName, String groupSearchDN, int timeoutMilliseconds) {
super(ctx, boundName);
this.groupSearchDN = groupSearchDN;
this.timeoutMilliseconds = timeoutMilliseconds;
}
/**
@ -59,6 +61,7 @@ public class ActiveDirectoryConnection extends AbstractLdapConnection {
//Specify the search scope
groupsSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
groupsSearchCtls.setReturningAttributes(Strings.EMPTY_ARRAY); //we only need the entry DN
groupsSearchCtls.setTimeLimit(timeoutMilliseconds);
ImmutableList.Builder<String> groups = ImmutableList.builder();
try {

View File

@ -38,6 +38,7 @@ public class ActiveDirectoryConnectionFactory extends ConnectionFactory {
private final ImmutableMap<String, Serializable> sharedLdapEnv;
private final String userSearchDN;
private final String domainName;
private final int timeoutMilliseconds;
@Inject
public ActiveDirectoryConnectionFactory(Settings settings) {
@ -47,14 +48,14 @@ public class ActiveDirectoryConnectionFactory extends ConnectionFactory {
throw new ShieldSettingsException("Missing [" + AD_DOMAIN_NAME_SETTING + "] setting for active directory");
}
userSearchDN = settings.get(AD_USER_SEARCH_BASEDN_SETTING, buildDnFromDomain(domainName));
timeoutMilliseconds = (int) settings.getAsTime(TIMEOUT_LDAP_SETTING, TIMEOUT_DEFAULT).millis();
String[] ldapUrls = settings.getAsArray(URLS_SETTING, new String[] { "ldaps://" + domainName + ":636" });
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder()
.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
.put(Context.PROVIDER_URL, Strings.arrayToCommaDelimitedString(ldapUrls))
.put(JNDI_LDAP_CONNECT_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_READ_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_CONNECT_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_TCP_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_READ_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_TCP_READ_SETTING, TIMEOUT_DEFAULT).millis()))
.put("java.naming.ldap.attributes.binary", "tokenGroups")
.put(Context.REFERRAL, "follow");
@ -82,7 +83,7 @@ public class ActiveDirectoryConnectionFactory extends ConnectionFactory {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchCtls.setReturningAttributes(Strings.EMPTY_ARRAY);
searchCtls.setTimeLimit(timeoutMilliseconds);
String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))";
NamingEnumeration<SearchResult> results = ctx.search(userSearchDN, searchFilter, new Object[] { userPrincipal }, searchCtls);
@ -91,7 +92,7 @@ public class ActiveDirectoryConnectionFactory extends ConnectionFactory {
String name = entry.getNameInNamespace();
if (!results.hasMore()) {
return new ActiveDirectoryConnection(ctx, name, userSearchDN);
return new ActiveDirectoryConnection(ctx, name, userSearchDN, timeoutMilliseconds);
}
throw new ActiveDirectoryException("Search for user [" + userName + "] by principle name yielded multiple results");
}

View File

@ -34,16 +34,18 @@ public class LdapConnection extends AbstractLdapConnection {
private final String groupSearchDN;
private final boolean isGroupSubTreeSearch;
private final boolean isFindGroupsByAttribute;
protected final String groupAttribute = "memberOf";
private final String groupAttribute = "memberOf";
private final int timeoutMilliseconds;
/**
* This object is intended to be constructed by the LdapConnectionFactory
*/
LdapConnection(DirContext ctx, String boundName, boolean isFindGroupsByAttribute, boolean isGroupSubTreeSearch, String groupSearchDN) {
LdapConnection(DirContext ctx, String boundName, boolean isFindGroupsByAttribute, boolean isGroupSubTreeSearch, String groupSearchDN, int timeoutMilliseconds) {
super(ctx, boundName);
this.isGroupSubTreeSearch = isGroupSubTreeSearch;
this.groupSearchDN = groupSearchDN;
this.isFindGroupsByAttribute = isFindGroupsByAttribute;
this.timeoutMilliseconds = timeoutMilliseconds;
}
/**
@ -72,6 +74,7 @@ public class LdapConnection extends AbstractLdapConnection {
SearchControls search = new SearchControls();
search.setReturningAttributes(Strings.EMPTY_ARRAY);
search.setSearchScope(this.isGroupSubTreeSearch ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
search.setTimeLimit(timeoutMilliseconds);
//This could be made could be made configurable but it should cover all cases
String filter = "(&" +

View File

@ -40,7 +40,7 @@ public class LdapConnectionFactory extends ConnectionFactory {
protected final String groupSearchDN;
protected final boolean groupSubTreeSearch;
protected final boolean findGroupsByAttribute;
private final int timeoutMilliseconds;
@Inject()
public LdapConnectionFactory(Settings settings) {
@ -54,11 +54,13 @@ public class LdapConnectionFactory extends ConnectionFactory {
throw new ShieldSettingsException("Missing required ldap setting [" + URLS_SETTING + "]");
}
timeoutMilliseconds = (int) settings.getAsTime(TIMEOUT_LDAP_SETTING, TIMEOUT_DEFAULT).millis();
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder()
.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
.put(Context.PROVIDER_URL, Strings.arrayToCommaDelimitedString(ldapUrls))
.put(JNDI_LDAP_READ_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_READ_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_CONNECT_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_READ_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_TCP_READ_SETTING, TIMEOUT_DEFAULT).millis()))
.put(JNDI_LDAP_CONNECT_TIMEOUT, Long.toString(settings.getAsTime(TIMEOUT_TCP_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()))
.put(Context.REFERRAL, "follow");
LdapSslSocketFactory.configureJndiSSL(ldapUrls, builder);
@ -90,7 +92,7 @@ public class LdapConnectionFactory extends ConnectionFactory {
DirContext ctx = new InitialDirContext(ldapEnv);
//return the first good connection
return new LdapConnection(ctx, dn, findGroupsByAttribute, groupSubTreeSearch, groupSearchDN);
return new LdapConnection(ctx, dn, findGroupsByAttribute, groupSubTreeSearch, groupSearchDN, timeoutMilliseconds);
} catch (NamingException e) {
logger.warn("Failed ldap authentication with user template [{}], dn [{}]", e, template, dn);

View File

@ -28,8 +28,9 @@ public abstract class ConnectionFactory {
public static final String URLS_SETTING = "url";
public static final String JNDI_LDAP_READ_TIMEOUT = "com.sun.jndi.ldap.read.timeout";
public static final String JNDI_LDAP_CONNECT_TIMEOUT = "com.sun.jndi.ldap.connect.timeout";
public static final String TIMEOUT_CONNECTION_SETTING = "connect_timeout";
public static final String TIMEOUT_READ_SETTING = "read_timeout";
public static final String TIMEOUT_TCP_CONNECTION_SETTING = "timeout.tcp_connect";
public static final String TIMEOUT_TCP_READ_SETTING = "timeout.tcp_read";
public static final String TIMEOUT_LDAP_SETTING = "timeout.ldap_search";
public static final TimeValue TIMEOUT_DEFAULT = TimeValue.timeValueSeconds(5);
protected final ESLogger logger = Loggers.getLogger(getClass());

View File

@ -12,6 +12,7 @@ import org.elasticsearch.shield.authc.ldap.LdapConnectionFactory;
import org.elasticsearch.shield.authc.ldap.LdapConnectionTests;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapConnection;
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapTest;
import org.elasticsearch.shield.ssl.SSLService;
@ -70,6 +71,22 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
}
}
@Test
public void testTcpReadTimeout() {
Settings settings = ImmutableSettings.builder()
.put(buildAdSettings(AD_LDAP_URL, AD_DOMAIN))
.put(ConnectionFactory.TIMEOUT_TCP_READ_SETTING, "1ms")
.build();
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(settings);
try (AbstractLdapConnection ldap = connectionFactory.open("ironman", SecuredStringTests.build(PASSWORD))) {
List<String> groups = ldap.groups();
fail("The TCP connection should timeout before getting groups back");
} catch (ActiveDirectoryException e) {
assertThat(e.getCause().getMessage(), containsString("LDAP response read timed out"));
}
}
@Test
public void testAdAuth_avengers() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(

View File

@ -20,12 +20,10 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.util.List;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.*;
public class LdapConnectionTests extends LdapTest {
@Test(expected = LdapException.class, timeout = 2000) //if the LDAP timeout doesn't occur within 2 seconds, fail
public void testBindWithTimeout() throws Exception {
int randomPort = randomIntBetween(49152, 65525); // ephemeral port
@ -42,14 +40,20 @@ public class LdapConnectionTests extends LdapTest {
};
Settings settings = ImmutableSettings.builder()
.put(buildLdapSettings(ldapUrls, userTemplates, groupSearchBase, true))
.put(ConnectionFactory.TIMEOUT_CONNECTION_SETTING, "1ms") //1 millisecond
.put(ConnectionFactory.TIMEOUT_TCP_CONNECTION_SETTING, "1ms") //1 millisecond
.build();
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(settings);
String user = "Horatio Hornblower";
SecuredString userPass = SecuredStringTests.build("pass");
try (LdapConnection ldap = connectionFactory.open(user, userPass)) {
long start = System.currentTimeMillis();
try (LdapConnection connection = connectionFactory.open(user, userPass)) {
fail("expected connection timeout error here");
} catch (Throwable t) {
long time = System.currentTimeMillis() - start;
assertThat(time, lessThan(1000l));
assertThat(t, instanceOf(LdapException.class));
}
}
}
@ -126,23 +130,4 @@ public class LdapConnectionTests extends LdapTest {
assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
}
}
@Test(expected = LdapException.class, timeout = 2000) //if the LDAP timeout doesn't occur in 2 seconds, fail
public void testGroupLookupWithTimeout() {
String groupSearchBase = "o=sevenSeas";
String userTemplate = "cn={0},ou=people,o=sevenSeas";
Settings settings = ImmutableSettings.builder()
.put(buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, true))
.put(ConnectionFactory.TIMEOUT_READ_SETTING, "1ms") //1 millisecond
.build();
LdapConnectionFactory ldapFac = new LdapConnectionFactory(settings);
String user = "Horatio Hornblower";
SecuredString userPass = SecuredStringTests.build("pass");
try (LdapConnection ldap = ldapFac.open(user, userPass)) {
List<String> groups = ldap.getGroupsFromSearch(ldap.authenticatedUserDn());
}
}
}

View File

@ -6,7 +6,9 @@
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
@ -42,7 +44,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
}
@Test
public void test_standardLdapConnection_uid() {
public void testConnect() {
//openldap does not use cn as naming attributes by default
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
@ -58,4 +60,41 @@ public class OpenLdapTests extends ElasticsearchTestCase {
}
}
@Test
public void testTcpTimeout() {
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
Settings settings = ImmutableSettings.builder()
.put(LdapConnectionTests.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, true))
.put(ConnectionFactory.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond
.build();
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(settings);
try (LdapConnection ldap = connectionFactory.open("thor", SecuredStringTests.build(PASSWORD))){
ldap.groups();
fail("The TCP connection should timeout before getting groups back");
} catch (LdapException e) {
assertThat(e.getCause().getMessage(), containsString("LDAP response read timed out"));
}
}
@Test
public void testLdapTimeout() {
String groupSearchBase = "dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
Settings settings = ImmutableSettings.builder()
.put(LdapConnectionTests.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, true))
.put(ConnectionFactory.TIMEOUT_LDAP_SETTING, "1ms") //1 millisecond
.build();
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(settings);
try (LdapConnection ldap = connectionFactory.open("thor", SecuredStringTests.build(PASSWORD))){
ldap.groups();
fail("The server should timeout the group request");
} catch (LdapException e) {
assertThat(e.getCause().getMessage(), containsString("error code 32")); //openldap response for timeout
}
}
}