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:
parent
7788c833e0
commit
2ed4dd7fb6
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
Binary file not shown.
|
@ -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";
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue