tests: Ldap unit tests for GroupResolvers

This adds unit tests for the three new GroupResolvers.

Original commit: elastic/x-pack-elasticsearch@d303388696
This commit is contained in:
c-a-m 2015-01-20 16:23:21 -07:00
parent 4a7731099b
commit b3630c7ea9
4 changed files with 426 additions and 1 deletions

View File

@ -42,7 +42,7 @@ public abstract class ConnectionFactory<Connection extends AbstractLdapConnectio
public static final String TIMEOUT_LDAP_SETTING = "timeout.ldap_search";
public static final String HOSTNAME_VERIFICATION_SETTING = "hostname_verification";
public static final TimeValue TIMEOUT_DEFAULT = TimeValue.timeValueSeconds(5);
static final String JAVA_NAMING_LDAP_FACTORY_SOCKET = "java.naming.ldap.factory.socket";
public static final String JAVA_NAMING_LDAP_FACTORY_SOCKET = "java.naming.ldap.factory.socket";
private static final Pattern STARTS_WITH_LDAPS = Pattern.compile("^ldaps:.*", Pattern.CASE_INSENSITIVE);
private static final Pattern STARTS_WITH_LDAP = Pattern.compile("^ldap:.*", Pattern.CASE_INSENSITIVE);

View File

@ -0,0 +1,159 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.SearchScope;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Hashtable;
import java.util.List;
import java.util.regex.Pattern;
import static org.hamcrest.Matchers.*;
@Network
public class ActiveDirectoryGroupsResolverTests extends ElasticsearchTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
private InitialDirContext ldapContext;
@Before
public void setUp() throws Exception {
super.setUp();
Path keystore = Paths.get(ActiveDirectoryGroupsResolverTests.class.getResource("../support/ldap/ldaptrust.jks").toURI()).toAbsolutePath();
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", keystore)
.put("shield.ssl.keystore.password", "changeit")
.build()));
Hashtable<String, Serializable> ldapEnv = new Hashtable<>();
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, BRUCE_BANNER_DN);
ldapEnv.put(Context.SECURITY_CREDENTIALS, ActiveDirectoryFactoryTests.PASSWORD);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, ActiveDirectoryFactoryTests.AD_LDAP_URL);
ldapEnv.put(ConnectionFactory.JAVA_NAMING_LDAP_FACTORY_SOCKET, LdapSslSocketFactory.class.getName());
ldapEnv.put("java.naming.ldap.attributes.binary", "tokenGroups");
ldapEnv.put(Context.REFERRAL, "follow");
ldapContext = new InitialDirContext(ldapEnv);
}
@After
public void tearDown() throws Exception {
super.tearDown();
ldapContext.close();
LdapSslSocketFactory.clear();
}
@Test
public void testResolveSubTree() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("scope", SearchScope.SUB_TREE)
.build();
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists"),
containsString("Users"),
containsString("Domain Users"),
containsString("Supers")));
}
@Test
public void testResolveOneLevel() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("scope", SearchScope.ONE_LEVEL)
.put("base_dn", "CN=Builtin, DC=ad, DC=test, DC=elasticsearch,DC=com")
.build();
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, hasItem(containsString("Users")));
}
@Test
public void testResolveBaseLevel() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("scope", SearchScope.BASE)
.put("base_dn", "CN=Users, CN=Builtin, DC=ad, DC=test, DC=elasticsearch, DC=com")
.build();
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, hasItem(containsString("Users")));
}
@Test
public void testBuildGroupQuery() throws Exception {
//test a user with no assigned groups, other than the default groups
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users group
"S-1-5-21-3510024162-210737641-214529065-513" //Default Domain Users group
};
String query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapContext, "CN=Jarvis, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10));
assertValidSidQuery(query, expectedSids);
}
//test a user of one groups
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users group
"S-1-5-21-3510024162-210737641-214529065-513", //Default Domain Users group
"S-1-5-21-3510024162-210737641-214529065-1117"}; //Gods group
String query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapContext, "CN=Odin, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10));
assertValidSidQuery(query, expectedSids);
}
//test a user of many groups
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users Group
"S-1-5-21-3510024162-210737641-214529065-513", //Default Domain Users group
"S-1-5-21-3510024162-210737641-214529065-1123", //Supers
"S-1-5-21-3510024162-210737641-214529065-1110", //Philanthropists
"S-1-5-21-3510024162-210737641-214529065-1108", //Geniuses
"S-1-5-21-3510024162-210737641-214529065-1106", //SHIELD
"S-1-5-21-3510024162-210737641-214529065-1105"};//Avengers
String query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapContext, "CN=Bruce Banner, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10));
assertValidSidQuery(query, expectedSids);
}
}
private void assertValidSidQuery(String query, String[] expectedSids) {
Pattern sidQueryPattern = Pattern.compile("\\(\\|(\\(objectSid=S(-\\d+)+\\))+\\)");
assertThat("[" + query + "] didn't match the search filter pattern", sidQueryPattern.matcher(query).matches(), is(true));
for(String sid: expectedSids) {
assertThat(query, containsString(sid));
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.SearchScope;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Hashtable;
import java.util.List;
import static org.hamcrest.Matchers.*;
@Network
public class SearchGroupsResolverTest extends ElasticsearchTestCase {
public static final String BRUCE_BANNER_DN = "uid=hulk,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
private InitialDirContext ldapContext;
@Before
public void setup() throws Exception {
super.setUp();
Path keystore = Paths.get(SearchGroupsResolverTest.class.getResource("../support/ldap/ldaptrust.jks").toURI()).toAbsolutePath();
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", keystore)
.put("shield.ssl.keystore.password", "changeit")
.build()));
Hashtable<String, Serializable> ldapEnv = new Hashtable<>();
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, BRUCE_BANNER_DN);
ldapEnv.put(Context.SECURITY_CREDENTIALS, OpenLdapTests.PASSWORD);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, OpenLdapTests.OPEN_LDAP_URL);
ldapEnv.put(ConnectionFactory.JAVA_NAMING_LDAP_FACTORY_SOCKET, LdapSslSocketFactory.class.getName());
ldapEnv.put("java.naming.ldap.attributes.binary", "tokenGroups");
ldapEnv.put(Context.REFERRAL, "follow");
ldapContext = new InitialDirContext(ldapEnv);
}
@After
public void tearDown() throws Exception {
super.tearDown();
ldapContext.close();
LdapSslSocketFactory.clear();
}
@Test
public void testResolveSubTree() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
}
@Test
public void testResolveOneLevel() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("base_dn", "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("scope", SearchScope.ONE_LEVEL)
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
}
@Test
public void testResolveBase() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("base_dn", "cn=Avengers,ou=People,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("scope", SearchScope.BASE)
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, hasItem(containsString("Avengers")));
}
@Test
public void testResolveCustomFilter() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("filter", "(&(objectclass=posixGroup)(memberUID={0}))")
.put("user_attribute", "uid")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, "uid=selvig,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com", TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
assertThat(groups, hasItem(containsString("Geniuses")));
}
@Test
public void testReadUserAttribute() throws Exception {
{
Settings settings = ImmutableSettings.builder().put("user_attribute", "uid").build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
assertThat(resolver.readUserAttribute(ldapContext, BRUCE_BANNER_DN), is("hulk"));
}
{
Settings settings = ImmutableSettings.builder().put("user_attribute", "cn").build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
assertThat(resolver.readUserAttribute(ldapContext, BRUCE_BANNER_DN), is("Bruce Banner"));
}
try {
Settings settings = ImmutableSettings.builder().put("user_attribute", "doesntExists").build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
resolver.readUserAttribute(ldapContext, BRUCE_BANNER_DN);
fail("searching for a non-existing attribute should throw an LdapException");
} catch (LdapException e) {
assertThat(e.getMessage(), containsString("No results returned"));
}
try {
Settings settings = ImmutableSettings.builder().put("user_attribute", "userPassword").build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
resolver.readUserAttribute(ldapContext, BRUCE_BANNER_DN);
fail("searching for a binary attribute should throw an LdapException");
} catch (LdapException e) {
assertThat(e.getMessage(), containsString("is not of type String"));
}
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.shield.authc.active_directory.ActiveDirectoryFactoryTests;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
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.shield.support.NoOpLogger;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Hashtable;
import java.util.List;
import static org.hamcrest.Matchers.*;
@Network
public class UserAttributeGroupsResolverTest extends ElasticsearchTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
private InitialDirContext ldapContext;
@Before
public void setUp() throws Exception {
super.setUp();
Path keystore = Paths.get(UserAttributeGroupsResolverTest.class.getResource("../support/ldap/ldaptrust.jks").toURI()).toAbsolutePath();
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", keystore)
.put("shield.ssl.keystore.password", "changeit")
.build()));
Hashtable<String, Serializable> ldapEnv = new Hashtable<>();
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, BRUCE_BANNER_DN);
ldapEnv.put(Context.SECURITY_CREDENTIALS, ActiveDirectoryFactoryTests.PASSWORD);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, ActiveDirectoryFactoryTests.AD_LDAP_URL);
ldapEnv.put(ConnectionFactory.JAVA_NAMING_LDAP_FACTORY_SOCKET, LdapSslSocketFactory.class.getName());
ldapEnv.put("java.naming.ldap.attributes.binary", "tokenGroups");
ldapEnv.put(Context.REFERRAL, "follow");
ldapContext = new InitialDirContext(ldapEnv);
}
@After
public void tearDown() throws Exception {
super.tearDown();
ldapContext.close();
LdapSslSocketFactory.clear();
}
@Test
public void testResolve() throws Exception {
//falling back on the 'memberOf' attribute
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(ImmutableSettings.EMPTY);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
}
@Test
public void testResolveCustomGroupAttribute() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("user_group_attribute", "seeAlso")
.build();
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
assertThat(groups, hasItem(containsString("Avengers"))); //seeAlso only has Avengers
}
@Test
public void testResolveInvalidGroupAttribute() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("user_group_attribute", "doesntExist")
.build();
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(settings);
List<String> groups = resolver.resolve(ldapContext, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
assertThat(groups, empty());
}
}