ldap: Adds OpenLdap and Active Directory tests, and refactors SSLConfig

SSLConfig is split into SSLConfig and SSLTrustConfig.

OpenLdapTests and ActiveDirectory tests connect via TLS to EC2 instances.

Original commit: elastic/x-pack-elasticsearch@ea38e58dea
This commit is contained in:
c-a-m 2014-08-28 08:13:14 -06:00
parent 7788c833e0
commit 2ed4dd7fb6
14 changed files with 399 additions and 132 deletions

View File

@ -50,12 +50,14 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
int port = componentSettings.getAsInt(AD_PORT, 389);
String[] ldapUrls = componentSettings.getAsArray(URLS_SETTING, new String[] { "ldap://" + domainName + ":" + port });
sharedLdapEnv = ImmutableMap.<String, Serializable>builder()
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(Context.REFERRAL, "follow")
.build();
.put(Context.REFERRAL, "follow");
LdapSslSocketFactory.configureJndiSSL(ldapUrls, builder);
sharedLdapEnv = builder.build();
}
/**
@ -91,7 +93,7 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
throw new LdapException("Search for user [" + userName + "] by principle name yielded multiple results");
}
throw new LdapException("Search for user [" + userName + "] yielded no results");
throw new LdapException("Search for user [" + userName + "], search root [" + userSearchDN + "] yielded no results");
} catch (NamingException e) {
throw new LdapException("Unable to authenticate user [" + userName + "] to active directory domain ["+ domainName +"]", e);

View File

@ -28,6 +28,7 @@ public class LdapModule extends AbstractShieldModule.Node {
protected void configureNode() {
if (enabled) {
bind(Realm.class).annotatedWith(named(LdapRealm.TYPE)).to(LdapRealm.class).asEagerSingleton();
bind(LdapSslSocketFactory.class).asEagerSingleton();
bind(LdapGroupToRoleMapper.class).asEagerSingleton();
String mode = settings.getComponentSettings(LdapModule.class).get("mode", "ldap");
if ("ldap".equals(mode)) {

View File

@ -0,0 +1,108 @@
/*
* 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.ElasticsearchException;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.transport.ssl.SSLTrustConfig;
import javax.net.SocketFactory;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Locale;
/**
* This factory is needed for JNDI configuration for LDAP connections. It wraps a single instance of a static
* factory that is initiated by the settings constructor
*/
public class LdapSslSocketFactory extends SocketFactory {
private static SocketFactory socketFactory;
private static ESLogger logger = ESLoggerFactory.getLogger(LdapSslSocketFactory.class.getName());
/**
* This should only be invoked once to establish a static instance.
*/
@Inject
public LdapSslSocketFactory(Settings settings) {
if (socketFactory == null) {
logger.error("LdapSslSocketFactory already configured, this change could lead to threading issues");
}
Settings componentSettings = settings.getComponentSettings(getClass());
SSLTrustConfig sslConfig = new SSLTrustConfig(componentSettings, settings.getByPrefix("shield.ssl."));
socketFactory = sslConfig.createSSLSocketFactory();
}
/**
* This is invoked by JNDI and the returned SocketFactory must be an LdapSslSocketFactory object
* @return
*/
public static SocketFactory getDefault() {
return new LdapSslSocketFactory();
}
public static boolean initialized() {
return socketFactory != null;
}
LdapSslSocketFactory(){
if (socketFactory == null){
throw new ElasticsearchException("Attempt to construct an uninitialized LdapSslSocketFactory");
}
}
//The following methods are all wrappers around the static instance of socketFactory
@Override
public Socket createSocket(String s, int i) throws IOException {
return socketFactory.createSocket(s, i);
}
@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i2) throws IOException {
return socketFactory.createSocket(s, i, inetAddress, i2);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return socketFactory.createSocket(inetAddress, i);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress2, int i2) throws IOException {
return socketFactory.createSocket(inetAddress, i, inetAddress2, i2);
}
/**
* If one of the ldapUrls are SSL this will set the LdapSslSocketFactory as a socket provider on the
* @param ldapUrls
* @param builder set of jndi properties, that will
*/
public static ImmutableMap.Builder<String, Serializable> configureJndiSSL(String[] ldapUrls, ImmutableMap.Builder<String, Serializable> builder) {
boolean needsSSL = false;
for(String url: ldapUrls){
if (url.toLowerCase(Locale.getDefault()).startsWith("ldaps://")) {
needsSSL = true;
break;
}
}
if (needsSSL) {
if (socketFactory != null) {
builder.put("java.naming.ldap.factory.socket", LdapSslSocketFactory.class.getName());
} else {
logger.warn("LdapSslSocketFactory not initialized and won't be used for LDAP connections");
}
} else {
logger.debug("LdapSslSocketFactory not used for LDAP connections");
}
return builder;
}
}

View File

@ -51,12 +51,15 @@ public class StandardLdapConnectionFactory extends AbstractComponent implements
if (ldapUrls == null) {
throw new ShieldException("Missing required ldap setting [" + URLS_SETTING + "]");
}
sharedLdapEnv = ImmutableMap.<String, Serializable>builder()
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(Context.REFERRAL, "follow")
.build();
.put(Context.REFERRAL, "follow");
LdapSslSocketFactory.configureJndiSSL(ldapUrls, builder);
sharedLdapEnv = builder.build();
groupSearchDN = componentSettings.get(GROUP_SEARCH_BASEDN_SETTING);
findGroupsByAttribute = groupSearchDN == null;
groupSubTreeSearch = componentSettings.getAsBoolean(GROUP_SEARCH_SUBTREE_SETTING, false);
@ -85,7 +88,7 @@ public class StandardLdapConnectionFactory extends AbstractComponent implements
return new LdapConnection(ctx, dn, findGroupsByAttribute, groupSubTreeSearch, groupSearchDN);
} catch (NamingException e) {
logger.warn("Failed ldap authentication with user template [{}], dn [{}]", template, dn);
logger.warn("Failed ldap authentication with user template [{}], dn [{}]", e, template, dn );
}
}

View File

@ -29,41 +29,28 @@ public class SSLConfig {
private String[] ciphers;
public SSLConfig(Settings componentSettings, Settings defaultSettings) {
SSLTrustConfig sslTrustConfig = new SSLTrustConfig(componentSettings, defaultSettings);
this.clientAuth = componentSettings.getAsBoolean("require.client.auth", defaultSettings.getAsBoolean("require.client.auth", true));
String keyStore = componentSettings.get("keystore", defaultSettings.get("keystore", System.getProperty("javax.net.ssl.keyStore")));
String keyStorePassword = componentSettings.get("keystore_password", defaultSettings.get("keystore_password", System.getProperty("javax.net.ssl.keyStorePassword")));
String keyStoreAlgorithm = componentSettings.get("keystore_algorithm", defaultSettings.get("keystore_algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm")));
String trustStore = componentSettings.get("truststore", defaultSettings.get("truststore", System.getProperty("javax.net.ssl.trustStore")));
String trustStorePassword = componentSettings.get("truststore_password", defaultSettings.get("truststore_password", System.getProperty("javax.net.ssl.trustStorePassword")));
String trustStoreAlgorithm = componentSettings.get("truststore_algorithm", defaultSettings.get("truststore_algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm")));
this.ciphers = componentSettings.getAsArray("ciphers", defaultSettings.getAsArray("ciphers", DEFAULT_CIPHERS));
if (keyStore == null) {
throw new ElasticsearchException("SSL Enabled, but keystore unconfigured");
}
if (trustStore == null) {
throw new ElasticsearchException("SSL Enabled, but truststore unconfigured");
}
if (keyStoreAlgorithm == null) {
keyStoreAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
}
if (trustStoreAlgorithm == null) {
trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
}
logger.debug("using keyStore[{}], keyAlgorithm[{}], trustStore[{}], trustAlgorithm[{}]", keyStore, keyStoreAlgorithm, trustStore, trustStoreAlgorithm);
logger.debug("using keyStore[{}], keyAlgorithm[{}], ", keyStore, keyStoreAlgorithm);
if (!new File(keyStore).exists()) {
throw new ElasticsearchSSLException("Keystore at path ["+ keyStore +"] does not exist");
}
if (!new File(trustStore).exists()) {
throw new ElasticsearchSSLException("Truststore at path ["+ keyStore +"] does not exist");
}
KeyStore ks = null;
KeyManagerFactory kmf = null;
try (FileInputStream in = new FileInputStream(keyStore)){
@ -78,26 +65,11 @@ public class SSLConfig {
throw new ElasticsearchSSLException("Failed to initialize a KeyManagerFactory", e);
}
TrustManager[] trustManagers = null;
try (FileInputStream in = new FileInputStream(trustStore)) {
// Load TrustStore
ks.load(in, trustStorePassword.toCharArray());
// Initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
trustFactory.init(ks);
// Retrieve the trust managers from the factory
trustManagers = trustFactory.getTrustManagers();
} catch (Exception e) {
throw new ElasticsearchException("Failed to initialize a TrustManagerFactory", e);
}
// Initialize sslContext
try {
String algorithm = componentSettings.get("context_algorithm", defaultSettings.get("shield.ssl.context_algorithm", "TLS"));
sslContext = SSLContext.getInstance(algorithm);
sslContext.init(kmf.getKeyManagers(), trustManagers, null);
sslContext.init(kmf.getKeyManagers(), sslTrustConfig.getTrustManagers(), null);
} catch (Exception e) {
throw new ElasticsearchSSLException("Failed to initialize the SSLContext", e);
}

View File

@ -0,0 +1,82 @@
/*
* 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.transport.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
/**
*
*/
public class SSLTrustConfig {
private static final ESLogger logger = Loggers.getLogger(SSLTrustConfig.class);
private final TrustManager[] trustManagers;
private final String sslContextAlgorithm;
private SSLContext sslContext;
public SSLTrustConfig(Settings componentSettings, Settings defaultSettings) {
this.sslContextAlgorithm = componentSettings.get("context_algorithm", defaultSettings.get("shield.ssl.context_algorithm", "TLS"));
String trustStore = componentSettings.get("truststore", defaultSettings.get("truststore", System.getProperty("javax.net.ssl.trustStore")));
String trustStorePassword = componentSettings.get("truststore_password", defaultSettings.get("truststore_password", System.getProperty("javax.net.ssl.trustStorePassword")));
String trustStoreAlgorithm = componentSettings.get("truststore_algorithm", defaultSettings.get("truststore_algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm")));
if (trustStore == null) {
throw new ElasticsearchException("SSL Enabled, but truststore unconfigured");
}
if (trustStoreAlgorithm == null) {
trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
}
logger.debug("using trustStore[{}], trustAlgorithm[{}]", trustStore, trustStoreAlgorithm);
if (!new File(trustStore).exists()) {
throw new ElasticsearchSSLException("Truststore at path ["+ trustStore +"] does not exist");
}
try (FileInputStream in = new FileInputStream(trustStore)) {
// Load TrustStore
KeyStore ks = KeyStore.getInstance("jks");
ks.load(in, trustStorePassword == null ? null : trustStorePassword.toCharArray());
// Initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
trustFactory.init(ks);
// Retrieve the trust managers from the factory
trustManagers = trustFactory.getTrustManagers();
} catch (Exception e) {
throw new ElasticsearchException("Failed to initialize a TrustManagerFactory", e);
}
}
public SSLSocketFactory createSSLSocketFactory() {
// Initialize sslContext
try {
sslContext = SSLContext.getInstance(sslContextAlgorithm);
sslContext.init(null, trustManagers, null);
} catch (Exception e) {
throw new ElasticsearchSSLException("Failed to initialize the SSLContext", e);
}
return sslContext.getSocketFactory();
}
public TrustManager[] getTrustManagers() {
return trustManagers;
}
}

View File

@ -7,62 +7,130 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Ignore;
import org.elasticsearch.test.junit.annotations.Network;
import org.hamcrest.Matchers;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.net.URISyntaxException;
import java.util.List;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
public static final String OPEN_LDAP_URL = "ldap://ad.es.com:389";
public static final String AD_LDAP_URL = "ldap://54.213.145.20:389";
public static final String PASSWORD = "4joD8LmWcrEfRa&p";
public static final String AD_LDAP_URL = "ldaps://54.213.145.20:636";
public static final String PASSWORD = "NickFuryHeartsES";
public static final String AD_DOMAIN = "ad.test.elasticsearch.com";
public static String SETTINGS_PREFIX = LdapRealm.class.getPackage().getName().substring("com.elasticsearch.".length()) + '.';
@Ignore
@Test
@BeforeClass
public static void setTrustStore() throws URISyntaxException {
new LdapSslSocketFactory(ImmutableSettings.builder()
.put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI()))
.build());
}
@Test @Network
public void testAdAuth() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, "ad.test.elasticsearch.com"));
buildAdSettings(AD_LDAP_URL, AD_DOMAIN));
String userName = "ironman";
LdapConnection ldap = connectionFactory.bind(userName, SecuredStringTests.build(PASSWORD));
String userDN = ldap.getAuthenticatedUserDn();
//System.out.println("userPassword check:"+ldap.checkPassword(userDn, userPass));
List<String> groups = ldap.getGroupsFromUserAttrs(userDN);
assertThat(groups, containsInAnyOrder(
containsString("Geniuses"),
containsString("Billionaire"),
containsString("Playboy"),
containsString("Philanthropists"),
containsString("Avengers"),
containsString("SHIELD")));
}
@Test @Network
public void testAdAuth_avengers() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN));
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow", };
for(String user: users) {
LdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD));
assertThat("group avenger test for user "+user, ldap.getGroups(), hasItem(Matchers.containsString("Avengers")));
ldap.close();
}
}
@Test @Network
public void testAdAuth_specificUserSearch() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN,
"CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com"));
String userName = "hulk";
LdapConnection ldap = connectionFactory.bind(userName, SecuredStringTests.build(PASSWORD));
String userDN = ldap.getAuthenticatedUserDn();
List<String> groups = ldap.getGroupsFromUserAttrs(userDN);
System.out.println("groups: "+groups);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
}
@Ignore
@Test
@Test @Network
public void testAD_standardLdapConnection(){
String groupSearchBase = "dc=ad,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "cn={0},cn=Users,dc=ad,dc=test,dc=elasticsearch,dc=com";
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
boolean isSubTreeSearch = true;
StandardLdapConnectionFactory connectionFactory = new StandardLdapConnectionFactory(
LdapTest.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, isSubTreeSearch));
String user = "Tony Stark";
String user = "Bruce Banner";
LdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD));
List<String> groups = ldap.getGroupsFromUserAttrs(ldap.getAuthenticatedUserDn());
List<String> groups2 = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
assertThat(groups, containsInAnyOrder(containsString("upchuckers"), containsString("localDistribution")));
assertThat(groups2, containsInAnyOrder(containsString("upchuckers"), containsString("localDistribution")));
System.out.println(groups);
System.out.println(groups2);
assertThat(groups, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
assertThat(groups2, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName) {
return ImmutableSettings.builder()
.putArray(LdapTest.SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(LdapTest.SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.putArray(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.build();
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN) {
return ImmutableSettings.builder()
.putArray(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
.build();
}
}

View File

@ -12,6 +12,8 @@ import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Rule;
import org.junit.Test;
import java.io.File;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;

View File

@ -7,28 +7,22 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class LdapRealmTest extends LdapTest {
public static final String AD_IP = "54.213.145.20";
public static final String AD_URL = "ldap://" + AD_IP + ":389";
public static final String VALID_USER_TEMPLATE = "cn={0},ou=people,o=sevenSeas";
public static final String VALID_USERNAME = "Thomas Masterman Hardy";
public static final String PASSWORD = "pass";
@ -51,7 +45,7 @@ public class LdapRealmTest extends LdapTest {
String groupSearchBase = "o=sevenSeas";
boolean isSubTreeSearch = true;
String userTemplate = VALID_USER_TEMPLATE;
Settings settings = LdapConnectionTests.buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch);
Settings settings = buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch);
StandardLdapConnectionFactory ldapFactory = new StandardLdapConnectionFactory(settings);
LdapRealm ldap = new LdapRealm(buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
@ -66,7 +60,7 @@ public class LdapRealmTest extends LdapTest {
boolean isSubTreeSearch = false;
String userTemplate = VALID_USER_TEMPLATE;
StandardLdapConnectionFactory ldapFactory = new StandardLdapConnectionFactory(
LdapConnectionTests.buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
LdapRealm ldap = new LdapRealm(buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
@ -81,7 +75,7 @@ public class LdapRealmTest extends LdapTest {
boolean isSubTreeSearch = true;
String userTemplate = VALID_USER_TEMPLATE;
StandardLdapConnectionFactory ldapFactory = new StandardLdapConnectionFactory(
LdapConnectionTests.buildLdapSettings( apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch) );
buildLdapSettings( apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch) );
ldapFactory = spy(ldapFactory);
LdapRealm ldap = new LdapRealm( buildCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
@ -98,7 +92,7 @@ public class LdapRealmTest extends LdapTest {
boolean isSubTreeSearch = true;
String userTemplate = VALID_USER_TEMPLATE;
StandardLdapConnectionFactory ldapFactory = new StandardLdapConnectionFactory(
LdapConnectionTests.buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch) );
buildLdapSettings(apacheDsRule.getUrl(), userTemplate, groupSearchBase, isSubTreeSearch) );
ldapFactory = spy(ldapFactory);
LdapRealm ldap = new LdapRealm( buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
@ -108,63 +102,4 @@ public class LdapRealmTest extends LdapTest {
//verify two and only two binds -> caching is disabled
verify(ldapFactory, times(2)).bind(anyString(), any(SecuredString.class));
}
@Ignore
@Test
public void testAD() {
String adDomain = "ad.test.elasticsearch.com";
String userSearchBaseDN = "dc=ad,dc=es,dc=com";
ActiveDirectoryConnectionFactory ldapFactory = new ActiveDirectoryConnectionFactory(
ActiveDirectoryFactoryTests.buildAdSettings(AD_URL, adDomain));
LdapRealm ldap = new LdapRealm( buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
User user = ldap.authenticate( new UsernamePasswordToken("george", SecuredStringTests.build("R))Tr0x")));
assertThat( user, notNullValue());
assertThat( user.roles(), hasItemInArray("upchuckers"));
}
@Ignore
@Test
public void testAD_defaults() {
//only set the adDomain, and see if it infers the rest correctly
String adDomain = AD_IP;
Settings settings = ImmutableSettings.builder()
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomain)
.build();
ActiveDirectoryConnectionFactory ldapFactory = new ActiveDirectoryConnectionFactory( settings );
LdapRealm ldap = new LdapRealm( buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(), restController);
User user = ldap.authenticate( new UsernamePasswordToken("george", SecuredStringTests.build("R))Tr0x")));
assertThat( user, notNullValue());
assertThat( user.roles(), hasItemInArray("upchuckers"));
}
private Settings buildNonCachingSettings() {
return ImmutableSettings.builder()
.put("shield.authc.ldap."+LdapRealm.CACHE_TTL, -1)
.build();
}
private Settings buildCachingSettings() {
return ImmutableSettings.builder()
.put("shield.authc.ldap."+LdapRealm.CACHE_TTL, 100000000)
.put("shield.authc.ldap." + LdapRealm.CACHE_MAX_USERS, 10)
.build();
}
private LdapGroupToRoleMapper buildGroupAsRoleMapper() {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.ldap." + LdapGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.build();
return new LdapGroupToRoleMapper(settings,
new Environment(settings),
new ResourceWatcherService(settings, new ThreadPool("test")));
}
}

View File

@ -7,7 +7,10 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.rules.RuleChain;
@ -36,4 +39,25 @@ public abstract class LdapTest extends ElasticsearchTestCase {
.put(SETTINGS_PREFIX + StandardLdapConnectionFactory.GROUP_SEARCH_BASEDN_SETTING, groupSearchBase)
.put(SETTINGS_PREFIX + StandardLdapConnectionFactory.GROUP_SEARCH_SUBTREE_SETTING, isSubTreeSearch).build();
}
protected Settings buildNonCachingSettings() {
return ImmutableSettings.builder()
.put("shield.authc.ldap."+LdapRealm.CACHE_TTL, -1)
.build();
}
protected Settings buildCachingSettings() {
return ImmutableSettings.builder()
.build();
}
protected LdapGroupToRoleMapper buildGroupAsRoleMapper() {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.ldap." + LdapGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.build();
return new LdapGroupToRoleMapper(settings,
new Environment(settings),
new ResourceWatcherService(settings, new ThreadPool("test")));
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.net.URISyntaxException;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
public class OpenLdapTests extends ElasticsearchTestCase {
public static final String OPEN_LDAP_URL = "ldaps://54.200.235.244:636";
public static final String PASSWORD = "NickFuryHeartsES";
public static final String SETTINGS_PREFIX = LdapRealm.class.getPackage().getName().substring("com.elasticsearch.".length()) + '.';
@BeforeClass
public static void setTrustStore() throws URISyntaxException {
//LdapModule will set this up as a singleton normally
new LdapSslSocketFactory(ImmutableSettings.builder()
.put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI()))
.build());
}
@Test @Network
public void test_standardLdapConnection_uid(){
//openldap does not use cn as naming attributes by default
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";
boolean isSubTreeSearch = true;
StandardLdapConnectionFactory connectionFactory = new StandardLdapConnectionFactory(
LdapConnectionTests.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, isSubTreeSearch));
String[] users = new String[]{"blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor"};
for(String user: users) {
LdapConnection ldap = connectionFactory.bind(user, PASSWORD.toCharArray());
assertThat(ldap.getGroups(), hasItem(containsString("Avengers")));
ldap.close();
}
}
}

View File

@ -74,6 +74,7 @@ public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest
.put("shield.transport.n2n.ip_filter.file", writeFile(folder, "ip_filter.yml", CONFIG_IPFILTER_ALLOW_ALL))
.put(getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode"))
.put("shield.audit.enabled", true)
.put(getSSLSettingsForLdap("/org/elasticsearch/shield/authc/ldap/ldaptrust.jks", "changeit"))
.put("plugins.load_classpath_plugins", false);
if (OsUtils.MAC) {
@ -148,6 +149,22 @@ public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest
return builder.build();
}
protected Settings getSSLSettingsForLdap(String resourcePathToStore, String password) {
File store;
try {
store = new File(getClass().getResource(resourcePathToStore).toURI());
assertThat(store.exists(), is(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
ImmutableSettings.Builder builder = settingsBuilder()
.put("shield.authc.ldap.truststore_password", password)
.put("shield.authc.ldap.truststore", store.getPath());
return builder.build();
}
protected File newFolder() {
try {
return tmpFolder.newFolder();

View File

@ -39,4 +39,6 @@ grant {
//this shouldn't be in a production environment, just to run tests:
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "setDefaultUncaughtExceptionHandler";
permission javax.net.ssl.SSLPermission "setDefaultSSLContext";
};