[ldap] add user search with base dn and bind dn
This adds a second mode of operation to the ldap realm. This mode of operation allows for single bind user to be specified. This bind user will be used to search for user DNs starting from a base DN. The user DN will then be used to authenticate via a bind operation. The bind user will then search for the user's groups. Closes elastic/elasticsearch#552 Closes elastic/elasticsearch#323 Original commit: elastic/x-pack-elasticsearch@3338730a64
This commit is contained in:
parent
d108faede3
commit
c2a61d2207
|
@ -35,7 +35,7 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
||||||
this.scope = LdapSearchScope.resolve(settings.get("scope"), LdapSearchScope.SUB_TREE);
|
this.scope = LdapSearchScope.resolve(settings.get("scope"), LdapSearchScope.SUB_TREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> resolve(LDAPConnection connection, String userDn, TimeValue timeout, ESLogger logger) {
|
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||||
Filter groupSearchFilter = buildGroupQuery(connection, userDn, timeout, logger);
|
Filter groupSearchFilter = buildGroupQuery(connection, userDn, timeout, logger);
|
||||||
logger.debug("group SID to DN search filter: [{}]", groupSearchFilter);
|
logger.debug("group SID to DN search filter: [{}]", groupSearchFilter);
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Filter buildGroupQuery(LDAPConnection connection, String userDn, TimeValue timeout, ESLogger logger) {
|
static Filter buildGroupQuery(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||||
try {
|
try {
|
||||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, "tokenGroups");
|
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, "tokenGroups");
|
||||||
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
||||||
|
|
|
@ -87,7 +87,7 @@ public class ActiveDirectorySessionFactory extends SessionFactory {
|
||||||
* @return An authenticated
|
* @return An authenticated
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LdapSession open(String userName, SecuredString password) {
|
public LdapSession session(String userName, SecuredString password) {
|
||||||
LDAPConnection connection;
|
LDAPConnection connection;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
package org.elasticsearch.shield.authc.ldap;
|
package org.elasticsearch.shield.authc.ldap;
|
||||||
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
|
import org.elasticsearch.shield.ShieldSettingsException;
|
||||||
import org.elasticsearch.shield.authc.RealmConfig;
|
import org.elasticsearch.shield.authc.RealmConfig;
|
||||||
import org.elasticsearch.shield.authc.ldap.support.AbstractLdapRealm;
|
import org.elasticsearch.shield.authc.ldap.support.AbstractLdapRealm;
|
||||||
import org.elasticsearch.shield.authc.ldap.support.GroupToRoleMapper;
|
import org.elasticsearch.shield.authc.ldap.support.GroupToRoleMapper;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
|
||||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
|
|
||||||
|
@ -20,7 +23,7 @@ public class LdapRealm extends AbstractLdapRealm {
|
||||||
|
|
||||||
public static final String TYPE = "ldap";
|
public static final String TYPE = "ldap";
|
||||||
|
|
||||||
public LdapRealm(RealmConfig config, LdapSessionFactory ldap, GroupToRoleMapper roleMapper) {
|
public LdapRealm(RealmConfig config, SessionFactory ldap, GroupToRoleMapper roleMapper) {
|
||||||
super(TYPE, config, ldap, roleMapper);
|
super(TYPE, config, ldap, roleMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +41,21 @@ public class LdapRealm extends AbstractLdapRealm {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LdapRealm create(RealmConfig config) {
|
public LdapRealm create(RealmConfig config) {
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
SessionFactory sessionFactory = sessionFactory(config, clientSSLService);
|
||||||
GroupToRoleMapper roleMapper = new GroupToRoleMapper(TYPE, config, watcherService, null);
|
GroupToRoleMapper roleMapper = new GroupToRoleMapper(TYPE, config, watcherService, null);
|
||||||
return new LdapRealm(config, sessionFactory, roleMapper);
|
return new LdapRealm(config, sessionFactory, roleMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) {
|
||||||
|
Settings searchSettings = config.settings().getAsSettings("user_search");
|
||||||
|
if (!searchSettings.names().isEmpty()) {
|
||||||
|
if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
|
||||||
|
throw new ShieldSettingsException("settings were found for both user search and user template modes of operation. Please remove the settings for the\n"
|
||||||
|
+ "mode you do not wish to use. For more details refer to the ldap authentication section of the Shield guide.");
|
||||||
|
}
|
||||||
|
return new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||||
|
}
|
||||||
|
return new LdapSessionFactory(config, clientSSLService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class LdapSessionFactory extends SessionFactory {
|
||||||
* @return authenticated exception
|
* @return authenticated exception
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LdapSession open(String username, SecuredString password) {
|
public LdapSession session(String username, SecuredString password) {
|
||||||
LDAPConnection connection;
|
LDAPConnection connection;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -115,7 +115,7 @@ public class LdapSessionFactory extends SessionFactory {
|
||||||
return MessageFormat.format(template, escapedUsername);
|
return MessageFormat.format(template, escapedUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LdapSession.GroupsResolver groupResolver(Settings settings) {
|
static GroupsResolver groupResolver(Settings settings) {
|
||||||
Settings searchSettings = settings.getAsSettings("group_search");
|
Settings searchSettings = settings.getAsSettings("group_search");
|
||||||
if (!searchSettings.names().isEmpty()) {
|
if (!searchSettings.names().isEmpty()) {
|
||||||
return new SearchGroupsResolver(searchSettings);
|
return new SearchGroupsResolver(searchSettings);
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* 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 com.unboundid.ldap.sdk.*;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
|
import org.elasticsearch.common.primitives.Ints;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.shield.ShieldSettingsException;
|
||||||
|
import org.elasticsearch.shield.authc.RealmConfig;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapSession;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapSession.GroupsResolver;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
|
||||||
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
|
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static com.unboundid.ldap.sdk.Filter.createEqualityFilter;
|
||||||
|
import static com.unboundid.ldap.sdk.Filter.encodeValue;
|
||||||
|
import static org.elasticsearch.shield.authc.ldap.support.LdapUtils.searchForEntry;
|
||||||
|
|
||||||
|
public class LdapUserSearchSessionFactory extends SessionFactory {
|
||||||
|
|
||||||
|
static final int DEFAULT_CONNECTION_POOL_SIZE = 20;
|
||||||
|
static final int DEFAULT_CONNECTION_POOL_INITIAL_SIZE = 5;
|
||||||
|
static final String DEFAULT_USERNAME_ATTRIBUTE = "uid";
|
||||||
|
static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
|
||||||
|
|
||||||
|
private final GroupsResolver groupResolver;
|
||||||
|
private final LDAPConnectionPool connectionPool;
|
||||||
|
private final String userSearchBaseDn;
|
||||||
|
private final LdapSearchScope scope;
|
||||||
|
private final String userAttribute;
|
||||||
|
private final ServerSet serverSet;
|
||||||
|
|
||||||
|
public LdapUserSearchSessionFactory(RealmConfig config, ClientSSLService sslService) {
|
||||||
|
super(config);
|
||||||
|
Settings settings = config.settings();
|
||||||
|
userSearchBaseDn = settings.get("user_search.base_dn");
|
||||||
|
if (userSearchBaseDn == null) {
|
||||||
|
throw new ShieldSettingsException("user_search base_dn must be specified");
|
||||||
|
}
|
||||||
|
scope = LdapSearchScope.resolve(settings.get("user_search.scope"), LdapSearchScope.SUB_TREE);
|
||||||
|
userAttribute = settings.get("user_search.attribute", DEFAULT_USERNAME_ATTRIBUTE);
|
||||||
|
serverSet = serverSet(settings, sslService);
|
||||||
|
connectionPool = connectionPool(config.settings(), serverSet, timeout);
|
||||||
|
groupResolver = groupResolver(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LDAPConnectionPool connectionPool(Settings settings, ServerSet serverSet, TimeValue timeout) {
|
||||||
|
SimpleBindRequest bindRequest = bindRequest(settings);
|
||||||
|
int initialSize = settings.getAsInt("user_search.pool.initial_size", DEFAULT_CONNECTION_POOL_INITIAL_SIZE);
|
||||||
|
int size = settings.getAsInt("user_search.pool.size", DEFAULT_CONNECTION_POOL_SIZE);
|
||||||
|
try {
|
||||||
|
LDAPConnectionPool pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
|
||||||
|
pool.setRetryFailedOperationsDueToInvalidConnections(true);
|
||||||
|
if (settings.getAsBoolean("user_search.pool.health_check.enabled", true)) {
|
||||||
|
String entryDn = settings.get("user_search.pool.health_check.dn", (bindRequest == null) ? null : bindRequest.getBindDN());
|
||||||
|
if (entryDn == null) {
|
||||||
|
pool.close();
|
||||||
|
throw new ShieldSettingsException("[user_search.bind_dn] has not been specified so a value must be specified for [user_search.pool.health_check.dn] or [user_search.pool.health_check.enabled] must be set to false");
|
||||||
|
}
|
||||||
|
long healthCheckInterval = settings.getAsTime("user_search.pool.health_check.interval", DEFAULT_HEALTH_CHECK_INTERVAL).millis();
|
||||||
|
// Checks the status of the LDAP connection at a specified interval in the background. We do not check on
|
||||||
|
// on create as the LDAP server may require authentication to get an entry. We do not check on checkout
|
||||||
|
// as we always set retry operations and the pool will handle a bad connection without the added latency on every operation
|
||||||
|
GetEntryLDAPConnectionPoolHealthCheck healthCheck = new GetEntryLDAPConnectionPoolHealthCheck(entryDn, timeout.millis(), false, false, false, true, false);
|
||||||
|
pool.setHealthCheck(healthCheck);
|
||||||
|
pool.setHealthCheckIntervalMillis(healthCheckInterval);
|
||||||
|
}
|
||||||
|
return pool;
|
||||||
|
} catch (LDAPException e) {
|
||||||
|
throw new ShieldLdapException("unable to connect to any LDAP servers", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleBindRequest bindRequest(Settings settings) {
|
||||||
|
SimpleBindRequest request = null;
|
||||||
|
String bindDn = settings.get("user_search.bind_dn");
|
||||||
|
if (bindDn != null) {
|
||||||
|
request = new SimpleBindRequest(bindDn, settings.get("user_search.bind_password"));
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerSet serverSet(Settings settings, ClientSSLService clientSSLService) {
|
||||||
|
// Parse LDAP urls
|
||||||
|
String[] ldapUrls = settings.getAsArray(URLS_SETTING);
|
||||||
|
if (ldapUrls == null || ldapUrls.length == 0) {
|
||||||
|
throw new ShieldSettingsException("missing required LDAP setting [" + URLS_SETTING + "]");
|
||||||
|
}
|
||||||
|
LDAPServers servers = new LDAPServers(ldapUrls);
|
||||||
|
LDAPConnectionOptions options = connectionOptions(settings);
|
||||||
|
SocketFactory socketFactory;
|
||||||
|
if (servers.ssl()) {
|
||||||
|
socketFactory = clientSSLService.sslSocketFactory();
|
||||||
|
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_SETTING, true)) {
|
||||||
|
logger.debug("using encryption for LDAP connections with hostname verification");
|
||||||
|
} else {
|
||||||
|
logger.debug("using encryption for LDAP connections without hostname verification");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
socketFactory = null;
|
||||||
|
}
|
||||||
|
FailoverServerSet serverSet = new FailoverServerSet(servers.addresses(), servers.ports(), socketFactory, options);
|
||||||
|
serverSet.setReOrderOnFailover(true);
|
||||||
|
return serverSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LdapSession session(String user, SecuredString password) {
|
||||||
|
SearchRequest request = new SearchRequest(userSearchBaseDn, scope.scope(), createEqualityFilter(userAttribute, encodeValue(user)), Strings.EMPTY_ARRAY);
|
||||||
|
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
||||||
|
try {
|
||||||
|
SearchResultEntry entry = searchForEntry(connectionPool, request, logger);
|
||||||
|
if (entry == null) {
|
||||||
|
throw new ShieldLdapException("failed to find user [" + user + "] with search base [" + userSearchBaseDn + "] scope [" + scope.toString().toLowerCase(Locale.ENGLISH) +"]");
|
||||||
|
}
|
||||||
|
String dn = entry.getDN();
|
||||||
|
tryBind(dn, password);
|
||||||
|
return new LdapSession(logger, connectionPool, dn, groupResolver, timeout);
|
||||||
|
} catch (LDAPException e) {
|
||||||
|
throw new ShieldLdapException("failed to authenticate user [" + user + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryBind(String dn, SecuredString password) {
|
||||||
|
LDAPConnection bindConnection;
|
||||||
|
try {
|
||||||
|
bindConnection = serverSet.getConnection();
|
||||||
|
} catch (LDAPException e) {
|
||||||
|
throw new ShieldLdapException("unable to connect to any LDAP servers for bind", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
bindConnection.bind(dn, new String(password.internalChars()));
|
||||||
|
} catch (LDAPException e) {
|
||||||
|
throw new ShieldLdapException("failed LDAP authentication", dn, e);
|
||||||
|
} finally {
|
||||||
|
bindConnection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method is used to cleanup the connections for tests
|
||||||
|
*/
|
||||||
|
void shutdown() {
|
||||||
|
connectionPool.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static GroupsResolver groupResolver(Settings settings) {
|
||||||
|
Settings searchSettings = settings.getAsSettings("group_search");
|
||||||
|
if (!searchSettings.names().isEmpty()) {
|
||||||
|
return new SearchGroupsResolver(searchSettings);
|
||||||
|
}
|
||||||
|
return new UserAttributeGroupsResolver(settings);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ class SearchGroupsResolver implements GroupsResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> resolve(LDAPConnection connection, String userDn, TimeValue timeout, ESLogger logger) {
|
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||||
List<String> groups = new LinkedList<>();
|
List<String> groups = new LinkedList<>();
|
||||||
|
|
||||||
String userId = userAttribute != null ? readUserAttribute(connection, userDn, timeout, logger) : userDn;
|
String userId = userAttribute != null ? readUserAttribute(connection, userDn, timeout, logger) : userDn;
|
||||||
|
@ -63,7 +63,7 @@ class SearchGroupsResolver implements GroupsResolver {
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
String readUserAttribute(LDAPConnection connection, String userDn, TimeValue timeout, ESLogger logger) {
|
String readUserAttribute(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||||
try {
|
try {
|
||||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, userAttribute);
|
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, userAttribute);
|
||||||
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
||||||
|
|
|
@ -35,7 +35,7 @@ class UserAttributeGroupsResolver implements GroupsResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> resolve(LDAPConnection connection, String userDn, TimeValue timeout, ESLogger logger) {
|
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||||
try {
|
try {
|
||||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, attribute);
|
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, attribute);
|
||||||
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
request.setTimeLimitSeconds(Ints.checkedCast(timeout.seconds()));
|
||||||
|
|
|
@ -39,7 +39,7 @@ public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected User doAuthenticate(UsernamePasswordToken token) {
|
protected User doAuthenticate(UsernamePasswordToken token) {
|
||||||
try (LdapSession session = sessionFactory.open(token.principal(), token.credentials())) {
|
try (LdapSession session = sessionFactory.session(token.principal(), token.credentials())) {
|
||||||
List<String> groupDNs = session.groups();
|
List<String> groupDNs = session.groups();
|
||||||
Set<String> roles = roleMapper.mapRoles(groupDNs);
|
Set<String> roles = roleMapper.mapRoles(groupDNs);
|
||||||
return new User.Simple(token.principal(), roles.toArray(new String[roles.size()]));
|
return new User.Simple(token.principal(), roles.toArray(new String[roles.size()]));
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.shield.authc.ldap.support;
|
package org.elasticsearch.shield.authc.ldap.support;
|
||||||
|
|
||||||
import com.unboundid.ldap.sdk.LDAPConnection;
|
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||||
|
import com.unboundid.ldap.sdk.LDAPInterface;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ import java.util.List;
|
||||||
public class LdapSession implements Closeable {
|
public class LdapSession implements Closeable {
|
||||||
|
|
||||||
protected final ESLogger logger;
|
protected final ESLogger logger;
|
||||||
protected final LDAPConnection ldapConnection;
|
protected final LDAPInterface ldap;
|
||||||
protected final String bindDn;
|
protected final String bindDn;
|
||||||
protected final GroupsResolver groupsResolver;
|
protected final GroupsResolver groupsResolver;
|
||||||
protected final TimeValue timeout;
|
protected final TimeValue timeout;
|
||||||
|
@ -31,9 +32,9 @@ public class LdapSession implements Closeable {
|
||||||
* outside of and be reused across all connections. We can't keep a static logger in this class
|
* outside of and be reused across all connections. We can't keep a static logger in this class
|
||||||
* since we want the logger to be contextual (i.e. aware of the settings and its environment).
|
* since we want the logger to be contextual (i.e. aware of the settings and its environment).
|
||||||
*/
|
*/
|
||||||
public LdapSession(ESLogger logger, LDAPConnection connection, String boundName, GroupsResolver groupsResolver, TimeValue timeout) {
|
public LdapSession(ESLogger logger, LDAPInterface connection, String boundName, GroupsResolver groupsResolver, TimeValue timeout) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.ldapConnection = connection;
|
this.ldap = connection;
|
||||||
this.bindDn = boundName;
|
this.bindDn = boundName;
|
||||||
this.groupsResolver = groupsResolver;
|
this.groupsResolver = groupsResolver;
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
|
@ -44,7 +45,10 @@ public class LdapSession implements Closeable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
ldapConnection.close();
|
// Only if it is an LDAPConnection do we need to close it
|
||||||
|
if (ldap instanceof LDAPConnection) {
|
||||||
|
((LDAPConnection) ldap).close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,12 +62,12 @@ public class LdapSession implements Closeable {
|
||||||
* @return List of fully distinguished group names
|
* @return List of fully distinguished group names
|
||||||
*/
|
*/
|
||||||
public List<String> groups() {
|
public List<String> groups() {
|
||||||
return groupsResolver.resolve(ldapConnection, bindDn, timeout, logger);
|
return groupsResolver.resolve(ldap, bindDn, timeout, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface GroupsResolver {
|
public static interface GroupsResolver {
|
||||||
|
|
||||||
List<String> resolve(LDAPConnection ldapConnection, String userDn, TimeValue timeout, ESLogger logger);
|
List<String> resolve(LDAPInterface ldapConnection, String userDn, TimeValue timeout, ESLogger logger);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,16 +39,16 @@ public final class LdapUtils {
|
||||||
/**
|
/**
|
||||||
* This method performs a LDAPConnection.search(...) operation while handling referral exceptions. This is necessary
|
* This method performs a LDAPConnection.search(...) operation while handling referral exceptions. This is necessary
|
||||||
* to maintain backwards compatibility
|
* to maintain backwards compatibility
|
||||||
* @param ldapConnection
|
* @param ldap
|
||||||
* @param searchRequest
|
* @param searchRequest
|
||||||
* @param logger
|
* @param logger
|
||||||
* @return
|
* @return
|
||||||
* @throws LDAPException
|
* @throws LDAPException
|
||||||
*/
|
*/
|
||||||
public static SearchResult search(LDAPConnection ldapConnection, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
public static SearchResult search(LDAPInterface ldap, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
||||||
SearchResult results;
|
SearchResult results;
|
||||||
try {
|
try {
|
||||||
results = ldapConnection.search(searchRequest);
|
results = ldap.search(searchRequest);
|
||||||
} catch (LDAPSearchException e) {
|
} catch (LDAPSearchException e) {
|
||||||
if (e.getResultCode().equals(ResultCode.REFERRAL) && e.getSearchResult() != null) {
|
if (e.getResultCode().equals(ResultCode.REFERRAL) && e.getSearchResult() != null) {
|
||||||
if (logger.isDebugEnabled()){
|
if (logger.isDebugEnabled()){
|
||||||
|
@ -65,16 +65,16 @@ public final class LdapUtils {
|
||||||
/**
|
/**
|
||||||
* This method performs a LDAPConnection.searchForEntry(...) operation while handling referral exceptions. This is necessary
|
* This method performs a LDAPConnection.searchForEntry(...) operation while handling referral exceptions. This is necessary
|
||||||
* to maintain backwards compatibility
|
* to maintain backwards compatibility
|
||||||
* @param ldapConnection
|
* @param ldap
|
||||||
* @param searchRequest
|
* @param searchRequest
|
||||||
* @param logger
|
* @param logger
|
||||||
* @return
|
* @return
|
||||||
* @throws LDAPException
|
* @throws LDAPException
|
||||||
*/
|
*/
|
||||||
public static SearchResultEntry searchForEntry(LDAPConnection ldapConnection, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
public static SearchResultEntry searchForEntry(LDAPInterface ldap, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
||||||
SearchResultEntry entry;
|
SearchResultEntry entry;
|
||||||
try {
|
try {
|
||||||
entry = ldapConnection.searchForEntry(searchRequest);
|
entry = ldap.searchForEntry(searchRequest);
|
||||||
} catch (LDAPSearchException e) {
|
} catch (LDAPSearchException e) {
|
||||||
if (e.getResultCode().equals(ResultCode.REFERRAL) && e.getSearchResult() != null && e.getSearchResult().getEntryCount() > 0) {
|
if (e.getResultCode().equals(ResultCode.REFERRAL) && e.getSearchResult() != null && e.getSearchResult().getEntryCount() > 0) {
|
||||||
if (logger.isDebugEnabled()){
|
if (logger.isDebugEnabled()){
|
||||||
|
|
|
@ -33,7 +33,7 @@ import static org.elasticsearch.common.collect.Iterables.all;
|
||||||
* A standard looking usage pattern could look like this:
|
* A standard looking usage pattern could look like this:
|
||||||
<pre>
|
<pre>
|
||||||
ConnectionFactory factory = ...
|
ConnectionFactory factory = ...
|
||||||
try (LdapConnection session = factory.open(...)) {
|
try (LdapConnection session = factory.session(...)) {
|
||||||
...do stuff with the session
|
...do stuff with the session
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -74,7 +74,7 @@ public abstract class SessionFactory {
|
||||||
* @param user The name of the user to authenticate the connection with.
|
* @param user The name of the user to authenticate the connection with.
|
||||||
* @param password The password of the user
|
* @param password The password of the user
|
||||||
*/
|
*/
|
||||||
public abstract LdapSession open(String user, SecuredString password);
|
public abstract LdapSession session(String user, SecuredString password);
|
||||||
|
|
||||||
protected static LDAPConnectionOptions connectionOptions(Settings settings) {
|
protected static LDAPConnectionOptions connectionOptions(Settings settings) {
|
||||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String userName = "ironman";
|
String userName = "ironman";
|
||||||
try (LdapSession ldap = sessionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
containsString("Geniuses"),
|
containsString("Geniuses"),
|
||||||
|
@ -85,7 +85,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
RealmConfig config = new RealmConfig("ad-test", settings);
|
RealmConfig config = new RealmConfig("ad-test", settings);
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
try (LdapSession ldap = sessionFactory.open("ironman", SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session("ironman", SecuredStringTests.build(PASSWORD))) {
|
||||||
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
||||||
ldap.groups();
|
ldap.groups();
|
||||||
fail("The TCP connection should timeout before getting groups back");
|
fail("The TCP connection should timeout before getting groups back");
|
||||||
|
@ -101,7 +101,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
|
|
||||||
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow", };
|
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow", };
|
||||||
for(String user: users) {
|
for(String user: users) {
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
assertThat("group avenger test for user "+user, ldap.groups(), hasItem(Matchers.containsString("Avengers")));
|
assertThat("group avenger test for user "+user, ldap.groups(), hasItem(Matchers.containsString("Avengers")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String userName = "hulk";
|
String userName = "hulk";
|
||||||
try (LdapSession ldap = sessionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
@ -135,7 +135,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String userName = "hulk";
|
String userName = "hulk";
|
||||||
try (LdapSession ldap = sessionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
@ -160,7 +160,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String userName = "hulk";
|
String userName = "hulk";
|
||||||
try (LdapSession ldap = sessionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
assertThat(groups, hasItem(containsString("Avengers")));
|
assertThat(groups, hasItem(containsString("Avengers")));
|
||||||
|
@ -175,7 +175,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
|
|
||||||
//Login with the UserPrincipalName
|
//Login with the UserPrincipalName
|
||||||
String userDN;
|
String userDN;
|
||||||
try (LdapSession ldap = sessionFactory.open("erik.selvig", SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session("erik.selvig", SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
userDN = ldap.authenticatedUserDn();
|
userDN = ldap.authenticatedUserDn();
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
@ -184,7 +184,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
containsString("Domain Users")));
|
containsString("Domain Users")));
|
||||||
}
|
}
|
||||||
//Same user but login with sAMAccountName
|
//Same user but login with sAMAccountName
|
||||||
try (LdapSession ldap = sessionFactory.open("selvig", SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session("selvig", SecuredStringTests.build(PASSWORD))) {
|
||||||
assertThat(ldap.authenticatedUserDn(), is(userDN));
|
assertThat(ldap.authenticatedUserDn(), is(userDN));
|
||||||
|
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
@ -205,7 +205,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
//Login with the UserPrincipalName
|
//Login with the UserPrincipalName
|
||||||
try (LdapSession ldap = sessionFactory.open("erik.selvig", SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session("erik.selvig", SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
containsString("Geniuses"),
|
containsString("Geniuses"),
|
||||||
|
@ -224,7 +224,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String user = "Bruce Banner";
|
String user = "Bruce Banner";
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
@ -243,7 +243,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String user = "Bruce Banner";
|
String user = "Bruce Banner";
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
assertThat(groups, containsInAnyOrder(
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
@ -260,7 +260,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String userName = "ironman";
|
String userName = "ironman";
|
||||||
try (LdapSession ldap = sessionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||||
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
||||||
} catch (ActiveDirectoryException e) {
|
} catch (ActiveDirectoryException e) {
|
||||||
assertThat(e.getMessage(), containsString("failed to connect to any active directory servers"));
|
assertThat(e.getMessage(), containsString("failed to connect to any active directory servers"));
|
||||||
|
@ -279,7 +279,7 @@ public class ActiveDirectorySessionFactoryTests extends ElasticsearchTestCase {
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String user = "Bruce Banner";
|
String user = "Bruce Banner";
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ 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.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
|
import org.elasticsearch.shield.ShieldSettingsException;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authc.RealmConfig;
|
import org.elasticsearch.shield.authc.RealmConfig;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
|
||||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||||
|
@ -22,8 +24,10 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.arrayContaining;
|
import static org.elasticsearch.shield.authc.ldap.LdapSessionFactory.USER_DN_TEMPLATES_SETTING;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.elasticsearch.shield.authc.ldap.support.SessionFactory.HOSTNAME_VERIFICATION_SETTING;
|
||||||
|
import static org.elasticsearch.shield.authc.ldap.support.SessionFactory.URLS_SETTING;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
@ -103,8 +107,8 @@ public class LdapRealmTest extends LdapTest {
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
|
|
||||||
//verify one and only one open -> caching is working
|
//verify one and only one session -> caching is working
|
||||||
verify(ldapFactory, times(1)).open(anyString(), any(SecuredString.class));
|
verify(ldapFactory, times(1)).session(anyString(), any(SecuredString.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -123,15 +127,15 @@ public class LdapRealmTest extends LdapTest {
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
|
|
||||||
//verify one and only one open -> caching is working
|
//verify one and only one session -> caching is working
|
||||||
verify(ldapFactory, times(1)).open(anyString(), any(SecuredString.class));
|
verify(ldapFactory, times(1)).session(anyString(), any(SecuredString.class));
|
||||||
|
|
||||||
roleMapper.notifyRefresh();
|
roleMapper.notifyRefresh();
|
||||||
|
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
|
|
||||||
//we need to open again
|
//we need to session again
|
||||||
verify(ldapFactory, times(2)).open(anyString(), any(SecuredString.class));
|
verify(ldapFactory, times(2)).session(anyString(), any(SecuredString.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -151,7 +155,62 @@ public class LdapRealmTest extends LdapTest {
|
||||||
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||||
|
|
||||||
//verify two and only two binds -> caching is disabled
|
//verify two and only two binds -> caching is disabled
|
||||||
verify(ldapFactory, times(2)).open(anyString(), any(SecuredString.class));
|
verify(ldapFactory, times(2)).session(anyString(), any(SecuredString.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLdapRealmSelectsLdapSessionFactory() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userTemplate = VALID_USER_TEMPLATE;
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.putArray(URLS_SETTING, ldapUrl())
|
||||||
|
.putArray(USER_DN_TEMPLATES_SETTING, userTemplate)
|
||||||
|
.put("group_search.base_dn", groupSearchBase)
|
||||||
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
|
.put(HOSTNAME_VERIFICATION_SETTING, false)
|
||||||
|
.build();
|
||||||
|
RealmConfig config = new RealmConfig("test-ldap-realm", settings);
|
||||||
|
SessionFactory sessionFactory = LdapRealm.Factory.sessionFactory(config, null);
|
||||||
|
assertThat(sessionFactory, is(instanceOf(LdapSessionFactory.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLdapRealmSelectsLdapUserSearchSessionFactory() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.putArray(URLS_SETTING, ldapUrl())
|
||||||
|
.put("user_search.base_dn", "")
|
||||||
|
.put("user_search.bind_dn", "cn=Thomas Masterman Hardy,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", PASSWORD)
|
||||||
|
.put("group_search.base_dn", groupSearchBase)
|
||||||
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
|
.put(HOSTNAME_VERIFICATION_SETTING, false)
|
||||||
|
.build();
|
||||||
|
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings);
|
||||||
|
SessionFactory sessionFactory = LdapRealm.Factory.sessionFactory(config, null);
|
||||||
|
try {
|
||||||
|
assertThat(sessionFactory, is(instanceOf(LdapUserSearchSessionFactory.class)));
|
||||||
|
} finally {
|
||||||
|
((LdapUserSearchSessionFactory)sessionFactory).shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.putArray(URLS_SETTING, ldapUrl())
|
||||||
|
.putArray(USER_DN_TEMPLATES_SETTING, "cn=foo")
|
||||||
|
.put("user_search.base_dn", "cn=bar")
|
||||||
|
.put("group_search.base_dn", "")
|
||||||
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
|
.put(HOSTNAME_VERIFICATION_SETTING, false)
|
||||||
|
.build();
|
||||||
|
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings);
|
||||||
|
try {
|
||||||
|
LdapRealm.Factory.sessionFactory(config, null);
|
||||||
|
fail("an exception should have been thrown because both user template and user search settings were specified");
|
||||||
|
} catch (ShieldSettingsException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("settings were found for both user search and user template"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
|
|
||||||
ldapServer.setProcessingDelayMillis(500L);
|
ldapServer.setProcessingDelayMillis(500L);
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
try (LdapSession session = sessionFactory.open(user, userPass)) {
|
try (LdapSession session = sessionFactory.session(user, userPass)) {
|
||||||
fail("expected connection timeout error here");
|
fail("expected connection timeout error here");
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
long time = System.currentTimeMillis() - start;
|
long time = System.currentTimeMillis() - start;
|
||||||
|
@ -73,7 +73,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
SecuredString userPass = SecuredStringTests.build("pass");
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
try (LdapSession session = sessionFactory.open(user, userPass)) {
|
try (LdapSession session = sessionFactory.session(user, userPass)) {
|
||||||
fail("expected connection timeout error here");
|
fail("expected connection timeout error here");
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
long time = System.currentTimeMillis() - start;
|
long time = System.currentTimeMillis() - start;
|
||||||
|
@ -98,7 +98,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
String user = "Horatio Hornblower";
|
String user = "Horatio Hornblower";
|
||||||
SecuredString userPass = SecuredStringTests.build("pass");
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
try (LdapSession ldap = sessionFactory.open(user, userPass)) {
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
String dn = ldap.authenticatedUserDn();
|
String dn = ldap.authenticatedUserDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
|
|
||||||
String user = "Horatio Hornblower";
|
String user = "Horatio Hornblower";
|
||||||
SecuredString userPass = SecuredStringTests.build("pass");
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
try (LdapSession ldapConnection = ldapFac.open(user, userPass)) {
|
try (LdapSession ldapConnection = ldapFac.session(user, userPass)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
String user = "Horatio Hornblower";
|
String user = "Horatio Hornblower";
|
||||||
SecuredString userPass = SecuredStringTests.build("pass");
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
try (LdapSession ldap = ldapFac.open(user, userPass)) {
|
try (LdapSession ldap = ldapFac.session(user, userPass)) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
||||||
|
|
||||||
String user = "Horatio Hornblower";
|
String user = "Horatio Hornblower";
|
||||||
try (LdapSession ldap = ldapFac.open(user, SecuredStringTests.build("pass"))) {
|
try (LdapSession ldap = ldapFac.session(user, SecuredStringTests.build("pass"))) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ public class LdapSessionFactoryTests extends LdapTest {
|
||||||
String user = "Horatio Hornblower";
|
String user = "Horatio Hornblower";
|
||||||
SecuredString userPass = SecuredStringTests.build("pass");
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
try (LdapSession ldap = ldapFac.open(user, userPass)) {
|
try (LdapSession ldap = ldapFac.session(user, userPass)) {
|
||||||
List<String> groups = ldap.groups();
|
List<String> groups = ldap.groups();
|
||||||
assertThat(groups.size(), is(1));
|
assertThat(groups.size(), is(1));
|
||||||
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
assertThat(groups, containsInAnyOrder("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
||||||
|
|
|
@ -0,0 +1,375 @@
|
||||||
|
/*
|
||||||
|
* 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 com.unboundid.ldap.sdk.*;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.shield.ShieldSettingsException;
|
||||||
|
import org.elasticsearch.shield.authc.RealmConfig;
|
||||||
|
import org.elasticsearch.shield.authc.activedirectory.ActiveDirectorySessionFactoryTests;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapSession;
|
||||||
|
import org.elasticsearch.shield.authc.ldap.support.LdapTest;
|
||||||
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
|
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||||
|
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
public class LdapUserSearchSessionFactoryTests extends LdapTest {
|
||||||
|
|
||||||
|
private ClientSSLService clientSSLService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initializeSslSocketFactory() throws Exception {
|
||||||
|
Path keystore = Paths.get(getResource("support/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.
|
||||||
|
*/
|
||||||
|
clientSSLService = new ClientSSLService(ImmutableSettings.builder()
|
||||||
|
.put("shield.ssl.keystore.path", keystore)
|
||||||
|
.put("shield.ssl.keystore.password", "changeit")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchSubTree() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
String dn = ldap.authenticatedUserDn();
|
||||||
|
assertThat(dn, containsString(user));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchBaseScopeFailsWithWrongBaseDN() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.scope", LdapSearchScope.BASE)
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
fail("the user should not have been found");
|
||||||
|
} catch (ShieldLdapException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("failed to find user [William Bush] with search base [o=sevenSeas] scope [base]"));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchBaseScopePassesWithCorrectBaseDN() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "cn=William Bush,ou=people,o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.scope", LdapSearchScope.BASE)
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
String dn = ldap.authenticatedUserDn();
|
||||||
|
assertThat(dn, containsString(user));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchOneLevelScopeFailsWithWrongBaseDN() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
fail("the user should not have been found");
|
||||||
|
} catch (ShieldLdapException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("failed to find user [William Bush] with search base [o=sevenSeas] scope [one_level]"));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchOneLevelScopePassesWithCorrectBaseDN() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "ou=people,o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
String dn = ldap.authenticatedUserDn();
|
||||||
|
assertThat(dn, containsString(user));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchWithBadAttributeFails() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.attribute", "uid1")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "William Bush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
fail("the user should not have been found");
|
||||||
|
} catch (ShieldLdapException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("failed to find user [William Bush] with search base [o=sevenSeas] scope [sub_tree]"));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchWithoutAttributePasses() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||||
|
|
||||||
|
String user = "wbush";
|
||||||
|
SecuredString userPass = SecuredStringTests.build("pass");
|
||||||
|
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, userPass)) {
|
||||||
|
String dn = ldap.authenticatedUserDn();
|
||||||
|
assertThat(dn, containsString("William Bush"));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchWithActiveDirectory() {
|
||||||
|
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||||
|
String userSearchBase = "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||||
|
Settings settings = settingsBuilder()
|
||||||
|
.put(LdapTest.buildLdapSettings(ActiveDirectorySessionFactoryTests.AD_LDAP_URL, Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "ironman@ad.test.elasticsearch.com")
|
||||||
|
.put("user_search.bind_password", ActiveDirectorySessionFactoryTests.PASSWORD)
|
||||||
|
.put("user_search.attribute", "cn")
|
||||||
|
.build();
|
||||||
|
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings);
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
|
String user = "Bruce Banner";
|
||||||
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(ActiveDirectorySessionFactoryTests.PASSWORD))) {
|
||||||
|
List<String> groups = ldap.groups();
|
||||||
|
|
||||||
|
assertThat(groups, containsInAnyOrder(
|
||||||
|
containsString("Avengers"),
|
||||||
|
containsString("SHIELD"),
|
||||||
|
containsString("Geniuses"),
|
||||||
|
containsString("Philanthropists")));
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserSearchwithBindUserOpenLDAP() {
|
||||||
|
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||||
|
String userSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||||
|
RealmConfig config = new RealmConfig("oldap-test", settingsBuilder()
|
||||||
|
.put(LdapTest.buildLdapSettings(OpenLdapTests.OPEN_LDAP_URL, Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.ONE_LEVEL))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "uid=blackwidow,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||||
|
.put("user_search.bind_password", OpenLdapTests.PASSWORD)
|
||||||
|
.build());
|
||||||
|
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
|
String[] users = new String[] { "cap", "hawkeye", "hulk", "ironman", "thor" };
|
||||||
|
try {
|
||||||
|
for (String user : users) {
|
||||||
|
LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(OpenLdapTests.PASSWORD));
|
||||||
|
assertThat(ldap.authenticatedUserDn(), is(equalTo(MessageFormat.format("uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com", user))));
|
||||||
|
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
|
||||||
|
ldap.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
sessionFactory.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConnectionPoolDefaultSettings() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.connectionPool(config.settings(), new SingleServerSet("localhost", ldapServer.getListenPort()), TimeValue.timeValueSeconds(5));
|
||||||
|
try {
|
||||||
|
assertThat(connectionPool.getCurrentAvailableConnections(), is(LdapUserSearchSessionFactory.DEFAULT_CONNECTION_POOL_INITIAL_SIZE));
|
||||||
|
assertThat(connectionPool.getMaximumAvailableConnections(), is(LdapUserSearchSessionFactory.DEFAULT_CONNECTION_POOL_SIZE));
|
||||||
|
assertEquals(connectionPool.getHealthCheck().getClass(), GetEntryLDAPConnectionPoolHealthCheck.class);
|
||||||
|
GetEntryLDAPConnectionPoolHealthCheck healthCheck = (GetEntryLDAPConnectionPoolHealthCheck) connectionPool.getHealthCheck();
|
||||||
|
assertThat(healthCheck.getEntryDN(), is("cn=Horatio Hornblower,ou=people,o=sevenSeas"));
|
||||||
|
assertThat(healthCheck.getMaxResponseTimeMillis(), is(LdapUserSearchSessionFactory.TIMEOUT_DEFAULT.millis()));
|
||||||
|
} finally {
|
||||||
|
connectionPool.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConnectionPoolSettings() throws Exception {
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.put("user_search.pool.initial_size", 10)
|
||||||
|
.put("user_search.pool.size", 12)
|
||||||
|
.put("user_search.pool.health_check.enabled", false)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.connectionPool(config.settings(), new SingleServerSet("localhost", ldapServer.getListenPort()), TimeValue.timeValueSeconds(5));
|
||||||
|
try {
|
||||||
|
assertThat(connectionPool.getCurrentAvailableConnections(), is(10));
|
||||||
|
assertThat(connectionPool.getMaximumAvailableConnections(), is(12));
|
||||||
|
assertThat(connectionPool.retryFailedOperationsDueToInvalidConnections(), is(true));
|
||||||
|
assertEquals(connectionPool.getHealthCheck().getClass(), LDAPConnectionPoolHealthCheck.class);
|
||||||
|
} finally {
|
||||||
|
connectionPool.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThatEmptyBindDNThrowsExceptionWithHealthCheckEnabled() throws Exception{
|
||||||
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", settingsBuilder()
|
||||||
|
.put(buildLdapSettings(ldapUrl(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase)
|
||||||
|
.put("user_search.bind_password", "pass")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
try {
|
||||||
|
new LdapUserSearchSessionFactory(config, null);
|
||||||
|
} catch (ShieldSettingsException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("[user_search.bind_dn] has not been specified so a value must be specified for [user_search.pool.health_check.dn] or [user_search.pool.health_check.enabled] must be set to false"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyBindDNReturnsNullBindRequest() {
|
||||||
|
BindRequest request = LdapUserSearchSessionFactory.bindRequest(settingsBuilder().put("user_search.bind_password", "password").build());
|
||||||
|
assertThat(request, is(nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThatBindRequestReturnsSimpleBindRequest() {
|
||||||
|
BindRequest request = LdapUserSearchSessionFactory.bindRequest(settingsBuilder()
|
||||||
|
.put("user_search.bind_password", "password")
|
||||||
|
.put("user_search.bind_dn", "cn=ironman")
|
||||||
|
.build());
|
||||||
|
assertEquals(request.getClass(), SimpleBindRequest.class);
|
||||||
|
SimpleBindRequest simpleBindRequest = (SimpleBindRequest) request;
|
||||||
|
assertThat(simpleBindRequest.getBindDN(), is("cn=ironman"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,7 @@ import org.junit.Test;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.hamcrest.Matchers.hasItem;
|
|
||||||
|
|
||||||
@Network
|
@Network
|
||||||
public class OpenLdapTests extends ElasticsearchTestCase {
|
public class OpenLdapTests extends ElasticsearchTestCase {
|
||||||
|
@ -59,7 +58,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) {
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
|
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +75,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) {
|
||||||
LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD));
|
LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD));
|
||||||
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
|
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
|
||||||
ldap.close();
|
ldap.close();
|
||||||
}
|
}
|
||||||
|
@ -94,7 +93,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
||||||
RealmConfig config = new RealmConfig("oldap-test", settings);
|
RealmConfig config = new RealmConfig("oldap-test", settings);
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
try (LdapSession ldap = sessionFactory.open("selvig", SecuredStringTests.build(PASSWORD))){
|
try (LdapSession ldap = sessionFactory.session("selvig", SecuredStringTests.build(PASSWORD))){
|
||||||
assertThat(ldap.groups(), hasItem(containsString("Geniuses")));
|
assertThat(ldap.groups(), hasItem(containsString("Geniuses")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +111,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
||||||
RealmConfig config = new RealmConfig("oldap-test", settings);
|
RealmConfig config = new RealmConfig("oldap-test", settings);
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
try (LdapSession ldap = sessionFactory.open("thor", SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session("thor", SecuredStringTests.build(PASSWORD))) {
|
||||||
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
||||||
ldap.groups();
|
ldap.groups();
|
||||||
fail("The TCP connection should timeout before getting groups back");
|
fail("The TCP connection should timeout before getting groups back");
|
||||||
|
@ -135,7 +134,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
||||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||||
|
|
||||||
String user = "blackwidow";
|
String user = "blackwidow";
|
||||||
try (LdapSession ldap = sessionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
|
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||||
fail("OpenLDAP certificate does not contain the correct hostname/ip so hostname verification should fail on open");
|
fail("OpenLDAP certificate does not contain the correct hostname/ip so hostname verification should fail on open");
|
||||||
} catch (ShieldLdapException e) {
|
} catch (ShieldLdapException e) {
|
||||||
assertThat(e.getMessage(), containsString("failed to connect to any LDAP servers"));
|
assertThat(e.getMessage(), containsString("failed to connect to any LDAP servers"));
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class SessionFactoryTests extends ElasticsearchTestCase {
|
||||||
return new SessionFactory(new RealmConfig("_name")) {
|
return new SessionFactory(new RealmConfig("_name")) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LdapSession open(String user, SecuredString password) {
|
public LdapSession session(String user, SecuredString password) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue