ldap: Changed LdapSslSocketFactory method of setting the static factory, plus miscellaneous cleanup

Original commit: elastic/x-pack-elasticsearch@1e1ba2aa7d
This commit is contained in:
c-a-m 2014-09-26 13:09:46 -06:00
parent 2ed4dd7fb6
commit a47de7539c
6 changed files with 42 additions and 49 deletions

View File

@ -27,8 +27,12 @@ public class LdapModule extends AbstractShieldModule.Node {
@Override @Override
protected void configureNode() { protected void configureNode() {
if (enabled) { if (enabled) {
/* This socket factory needs to be configured before any LDAP connections are created. LDAP configuration
for JNDI invokes a static getSocketFactory method from LdapSslSocketFactory. This doesn't mesh well with
guice so we set the factory here during startup. See LdapSslSocketFactory for more details. */
LdapSslSocketFactory.init(settings);
bind(Realm.class).annotatedWith(named(LdapRealm.TYPE)).to(LdapRealm.class).asEagerSingleton(); bind(Realm.class).annotatedWith(named(LdapRealm.TYPE)).to(LdapRealm.class).asEagerSingleton();
bind(LdapSslSocketFactory.class).asEagerSingleton();
bind(LdapGroupToRoleMapper.class).asEagerSingleton(); bind(LdapGroupToRoleMapper.class).asEagerSingleton();
String mode = settings.getComponentSettings(LdapModule.class).get("mode", "ldap"); String mode = settings.getComponentSettings(LdapModule.class).get("mode", "ldap");
if ("ldap".equals(mode)) { if ("ldap".equals(mode)) {

View File

@ -5,9 +5,7 @@
*/ */
package org.elasticsearch.shield.authc.ldap; package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
@ -22,23 +20,28 @@ import java.util.Locale;
/** /**
* This factory is needed for JNDI configuration for LDAP connections. It wraps a single instance of a static * 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 * factory that is initiated by the settings constructor. JNDI uses reflection to call the getDefault() static method
* then checks to make sure that the factory returned is an LdapSslSocketFactory. Because of this we have to wrap
* the socket factory
*
* http://docs.oracle.com/javase/tutorial/jndi/ldap/ssl.html
*/ */
public class LdapSslSocketFactory extends SocketFactory { public class LdapSslSocketFactory extends SocketFactory {
private static SocketFactory socketFactory;
private static ESLogger logger = ESLoggerFactory.getLogger(LdapSslSocketFactory.class.getName()); private static ESLogger logger = ESLoggerFactory.getLogger(LdapSslSocketFactory.class.getName());
private static LdapSslSocketFactory instance;
/** /**
* This should only be invoked once to establish a static instance. * This should only be invoked once to establish a static instance that will be used for each constructor.
*/ */
@Inject public static void init(Settings settings) {
public LdapSslSocketFactory(Settings settings) { if (instance != null) {
if (socketFactory == null) {
logger.error("LdapSslSocketFactory already configured, this change could lead to threading issues"); logger.error("LdapSslSocketFactory already configured, this change could lead to threading issues");
} }
Settings componentSettings = settings.getComponentSettings(getClass());
Settings componentSettings = settings.getComponentSettings(LdapSslSocketFactory.class);
SSLTrustConfig sslConfig = new SSLTrustConfig(componentSettings, settings.getByPrefix("shield.ssl.")); SSLTrustConfig sslConfig = new SSLTrustConfig(componentSettings, settings.getByPrefix("shield.ssl."));
socketFactory = sslConfig.createSSLSocketFactory(); instance = new LdapSslSocketFactory(sslConfig.createSSLSocketFactory());
} }
/** /**
@ -46,20 +49,21 @@ public class LdapSslSocketFactory extends SocketFactory {
* @return * @return
*/ */
public static SocketFactory getDefault() { public static SocketFactory getDefault() {
return new LdapSslSocketFactory(); assert instance != null;
return instance;
} }
public static boolean initialized() { public static boolean initialized() {
return socketFactory != null; return instance != null;
} }
LdapSslSocketFactory(){ final private SocketFactory socketFactory;
if (socketFactory == null){
throw new ElasticsearchException("Attempt to construct an uninitialized LdapSslSocketFactory"); private LdapSslSocketFactory(SocketFactory wrappedSocketFactory){
} socketFactory = wrappedSocketFactory;
} }
//The following methods are all wrappers around the static instance of socketFactory //The following methods are all wrappers around the instance of socketFactory
@Override @Override
public Socket createSocket(String s, int i) throws IOException { public Socket createSocket(String s, int i) throws IOException {
@ -82,11 +86,11 @@ public class LdapSslSocketFactory extends SocketFactory {
} }
/** /**
* If one of the ldapUrls are SSL this will set the LdapSslSocketFactory as a socket provider on the * If one of the ldapUrls are SSL this will set the LdapSslSocketFactory as a socket provider on the builder
* @param ldapUrls * @param ldapUrls
* @param builder set of jndi properties, that will * @param builder set of jndi properties, that will
*/ */
public static ImmutableMap.Builder<String, Serializable> configureJndiSSL(String[] ldapUrls, ImmutableMap.Builder<String, Serializable> builder) { public static void configureJndiSSL(String[] ldapUrls, ImmutableMap.Builder<String, Serializable> builder) {
boolean needsSSL = false; boolean needsSSL = false;
for(String url: ldapUrls){ for(String url: ldapUrls){
if (url.toLowerCase(Locale.getDefault()).startsWith("ldaps://")) { if (url.toLowerCase(Locale.getDefault()).startsWith("ldaps://")) {
@ -95,14 +99,10 @@ public class LdapSslSocketFactory extends SocketFactory {
} }
} }
if (needsSSL) { if (needsSSL) {
if (socketFactory != null) { assert instance != null : "LdapSslSocketFactory not initialized and won't be used for LDAP connections";
builder.put("java.naming.ldap.factory.socket", LdapSslSocketFactory.class.getName()); 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 { } else {
logger.debug("LdapSslSocketFactory not used for LDAP connections"); logger.debug("LdapSslSocketFactory not used for LDAP connections");
} }
return builder;
} }
} }

View File

@ -7,7 +7,6 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.SecuredStringTests; import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network; import org.elasticsearch.test.junit.annotations.Network;
@ -19,10 +18,9 @@ import java.io.File;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
@Network
public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase { public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
public static final String AD_LDAP_URL = "ldaps://54.213.145.20:636"; public static final String AD_LDAP_URL = "ldaps://54.213.145.20:636";
public static final String PASSWORD = "NickFuryHeartsES"; public static final String PASSWORD = "NickFuryHeartsES";
@ -31,13 +29,13 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
@BeforeClass @BeforeClass
public static void setTrustStore() throws URISyntaxException { public static void setTrustStore() throws URISyntaxException {
new LdapSslSocketFactory(ImmutableSettings.builder() LdapSslSocketFactory.init(ImmutableSettings.builder()
.put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI())) .put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI()))
.build()); .build());
} }
@Test @Network @Test
public void testAdAuth() { public void testAdAuth() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory( ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN)); buildAdSettings(AD_LDAP_URL, AD_DOMAIN));
@ -54,10 +52,9 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
containsString("Philanthropists"), containsString("Philanthropists"),
containsString("Avengers"), containsString("Avengers"),
containsString("SHIELD"))); containsString("SHIELD")));
} }
@Test @Network @Test
public void testAdAuth_avengers() { public void testAdAuth_avengers() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory( ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN)); buildAdSettings(AD_LDAP_URL, AD_DOMAIN));
@ -70,7 +67,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
} }
} }
@Test @Network @Test
public void testAdAuth_specificUserSearch() { public void testAdAuth_specificUserSearch() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory( ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN, buildAdSettings(AD_LDAP_URL, AD_DOMAIN,
@ -81,17 +78,15 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
String userDN = ldap.getAuthenticatedUserDn(); String userDN = ldap.getAuthenticatedUserDn();
List<String> groups = ldap.getGroupsFromUserAttrs(userDN); List<String> groups = ldap.getGroupsFromUserAttrs(userDN);
System.out.println("groups: "+groups);
assertThat(groups, containsInAnyOrder( assertThat(groups, containsInAnyOrder(
containsString("Avengers"), containsString("Avengers"),
containsString("SHIELD"), containsString("SHIELD"),
containsString("Geniuses"), containsString("Geniuses"),
containsString("Philanthropists"))); containsString("Philanthropists")));
} }
@Test @Network @Test
public void testAD_standardLdapConnection(){ public void testAD_standardLdapConnection(){
String groupSearchBase = "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"; String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
@ -105,8 +100,6 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
List<String> groups = ldap.getGroupsFromUserAttrs(ldap.getAuthenticatedUserDn()); List<String> groups = ldap.getGroupsFromUserAttrs(ldap.getAuthenticatedUserDn());
List<String> groups2 = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn()); List<String> groups2 = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
System.out.println(groups);
System.out.println(groups2);
assertThat(groups, containsInAnyOrder( assertThat(groups, containsInAnyOrder(
containsString("Avengers"), containsString("Avengers"),
containsString("SHIELD"), containsString("SHIELD"),

View File

@ -12,8 +12,6 @@ import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import java.io.File;
import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -76,7 +74,6 @@ public class LdapConnectionTests extends LdapTest {
LdapConnection ldap = ldapFac.bind(user, userPass); LdapConnection ldap = ldapFac.bind(user, userPass);
List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn()); List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
System.out.println("groups:"+groups);
assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas")); assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
} }
@ -92,7 +89,6 @@ public class LdapConnectionTests extends LdapTest {
LdapConnection ldap = ldapFac.bind(user, SecuredStringTests.build("pass")); LdapConnection ldap = ldapFac.bind(user, SecuredStringTests.build("pass"));
List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn()); List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
System.out.println("groups:"+groups);
assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas")); assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
} }
} }

View File

@ -6,6 +6,7 @@
package org.elasticsearch.shield.authc.ldap; package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network; import org.elasticsearch.test.junit.annotations.Network;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -17,6 +18,7 @@ import java.net.URISyntaxException;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
@Network
public class OpenLdapTests extends ElasticsearchTestCase { public class OpenLdapTests extends ElasticsearchTestCase {
public static final String OPEN_LDAP_URL = "ldaps://54.200.235.244:636"; public static final String OPEN_LDAP_URL = "ldaps://54.200.235.244:636";
public static final String PASSWORD = "NickFuryHeartsES"; public static final String PASSWORD = "NickFuryHeartsES";
@ -25,12 +27,12 @@ public class OpenLdapTests extends ElasticsearchTestCase {
@BeforeClass @BeforeClass
public static void setTrustStore() throws URISyntaxException { public static void setTrustStore() throws URISyntaxException {
//LdapModule will set this up as a singleton normally //LdapModule will set this up as a singleton normally
new LdapSslSocketFactory(ImmutableSettings.builder() LdapSslSocketFactory.init(ImmutableSettings.builder()
.put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI())) .put(SETTINGS_PREFIX + "truststore", new File(LdapConnectionTests.class.getResource("ldaptrust.jks").toURI()))
.build()); .build());
} }
@Test @Network @Test
public void test_standardLdapConnection_uid(){ public void test_standardLdapConnection_uid(){
//openldap does not use cn as naming attributes by default //openldap does not use cn as naming attributes by default
@ -42,7 +44,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
String[] users = new String[]{"blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor"}; String[] users = new String[]{"blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor"};
for(String user: users) { for(String user: users) {
LdapConnection ldap = connectionFactory.bind(user, PASSWORD.toCharArray()); LdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD));
assertThat(ldap.getGroups(), hasItem(containsString("Avengers"))); assertThat(ldap.getGroups(), hasItem(containsString("Avengers")));
ldap.close(); ldap.close();
} }

View File

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