LDAP: Changed AD group lookup to include nested groups
This expands the ActiveDirectoryConnectionFactory to lookup nested groups, in a non standard way. Fixes https://github.com/elasticsearch/elasticsearch-shield/issues/286 Original commit: elastic/x-pack-elasticsearch@01781a8305
This commit is contained in:
parent
34a69cd1cf
commit
852529bf53
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.collect.ImmutableList;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ActiveDirectoryConnection implements LdapConnection {
|
||||
private static final ESLogger logger = ESLoggerFactory.getLogger(GenericLdapConnection.class.getName());
|
||||
private final String bindDn;
|
||||
protected final DirContext ldapContext;
|
||||
|
||||
private final String groupSearchDN;
|
||||
protected final String groupAttribute = "memberOf";
|
||||
|
||||
/**
|
||||
* This object is intended to be constructed by the LdapConnectionFactory
|
||||
*/
|
||||
ActiveDirectoryConnection(DirContext ctx, String boundName, String groupSearchDN) {
|
||||
this.ldapContext = ctx;
|
||||
this.bindDn = boundName;
|
||||
this.groupSearchDN = groupSearchDN;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP connections should be closed to clean up resources. However, the jndi contexts have the finalize
|
||||
* implemented properly so that it will clean up on garbage collection.
|
||||
*/
|
||||
@Override
|
||||
public void close(){
|
||||
try {
|
||||
ldapContext.close();
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not close the LDAP connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getGroups() {
|
||||
|
||||
String groupsSearchFilter = buildGroupQuery();
|
||||
|
||||
// Search for groups the user belongs to in order to get their names
|
||||
//Create the search controls
|
||||
SearchControls groupsSearchCtls = new SearchControls();
|
||||
|
||||
//Specify the search scope
|
||||
groupsSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
//Specify the Base for the search
|
||||
String groupsSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
|
||||
//Specify the attributes to return
|
||||
String groupsReturnedAtts[]={};
|
||||
groupsSearchCtls.setReturningAttributes(groupsReturnedAtts);
|
||||
|
||||
ImmutableList.Builder<String> groups = ImmutableList.<String>builder();
|
||||
try {
|
||||
//Search for objects using the filter
|
||||
NamingEnumeration groupsAnswer = ldapContext.search(groupsSearchBase, groupsSearchFilter.toString(), groupsSearchCtls);
|
||||
|
||||
//Loop through the search results
|
||||
while (groupsAnswer.hasMoreElements()) {
|
||||
SearchResult sr = (SearchResult) groupsAnswer.next();
|
||||
groups.add(sr.getNameInNamespace());
|
||||
}
|
||||
} catch (NamingException ne) {
|
||||
throw new LdapException("Exception occurred fetching AD groups", bindDn, ne);
|
||||
}
|
||||
return groups.build();
|
||||
}
|
||||
|
||||
private String buildGroupQuery() {
|
||||
StringBuffer groupsSearchFilter = new StringBuffer("(|");
|
||||
try {
|
||||
SearchControls userSearchCtls = new SearchControls();
|
||||
userSearchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||
|
||||
//specify the LDAP search filter to find the user in question
|
||||
String userSearchFilter = "(objectClass=user)";
|
||||
String userReturnedAtts[] = { "tokenGroups" };
|
||||
userSearchCtls.setReturningAttributes(userReturnedAtts);
|
||||
NamingEnumeration userAnswer = ldapContext.search(getAuthenticatedUserDn(), userSearchFilter, userSearchCtls);
|
||||
|
||||
//Loop through the search results
|
||||
while (userAnswer.hasMoreElements()) {
|
||||
|
||||
SearchResult sr = (SearchResult)userAnswer.next();
|
||||
Attributes attrs = sr.getAttributes();
|
||||
|
||||
if (attrs != null) {
|
||||
for (NamingEnumeration ae = attrs.getAll();ae.hasMore();) {
|
||||
Attribute attr = (Attribute)ae.next();
|
||||
for (NamingEnumeration e = attr.getAll();e.hasMore();) {
|
||||
byte[] sid = (byte[])e.next();
|
||||
groupsSearchFilter.append("(objectSid=" + binarySidToStringSid(sid) + ")");
|
||||
}
|
||||
groupsSearchFilter.append(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NamingException ne) {
|
||||
throw new LdapException("Exception occurred fetching AD groups", bindDn, ne);
|
||||
}
|
||||
return groupsSearchFilter.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticatedUserDn() {
|
||||
return bindDn;
|
||||
}
|
||||
|
||||
/**
|
||||
* No idea whats going on here. Its copied from here:
|
||||
* http://blogs.msdn.com/b/alextch/archive/2007/06/18/sample-java-application-that-retrieves-group-membership-of-an-active-directory-user-account.aspx
|
||||
* @param SID byte encoded security ID
|
||||
* @return
|
||||
*/
|
||||
public String binarySidToStringSid( byte[] SID ) {
|
||||
String strSID = "";
|
||||
|
||||
//convert the SID into string format
|
||||
|
||||
long version;
|
||||
long authority;
|
||||
long count;
|
||||
long rid;
|
||||
|
||||
strSID = "S";
|
||||
version = SID[0];
|
||||
strSID = strSID + "-" + Long.toString(version);
|
||||
authority = SID[4];
|
||||
|
||||
for (int i = 0;i<4;i++) {
|
||||
authority <<= 8;
|
||||
authority += SID[4+i] & 0xFF;
|
||||
}
|
||||
|
||||
strSID = strSID + "-" + Long.toString(authority);
|
||||
count = SID[2];
|
||||
count <<= 8;
|
||||
count += SID[1] & 0xFF;
|
||||
for (int j=0;j<count;j++) {
|
||||
rid = SID[11 + (j*4)] & 0xFF;
|
||||
for (int k=1;k<4;k++) {
|
||||
rid <<= 8;
|
||||
rid += SID[11-k + (j*4)] & 0xFF;
|
||||
}
|
||||
strSID = strSID + "-" + Long.toString(rid);
|
||||
}
|
||||
return strSID;
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
|
|||
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("java.naming.ldap.attributes.binary", "tokenGroups")
|
||||
.put(Context.REFERRAL, "follow");
|
||||
|
||||
LdapSslSocketFactory.configureJndiSSL(ldapUrls, builder);
|
||||
|
@ -66,7 +67,7 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
|
|||
* @return An authenticated
|
||||
*/
|
||||
@Override
|
||||
public LdapConnection bind(String userName, SecuredString password) {
|
||||
public ActiveDirectoryConnection bind(String userName, SecuredString password) {
|
||||
String userPrincipal = userName + "@" + this.domainName;
|
||||
Hashtable<String, Serializable> ldapEnv = new Hashtable<>(this.sharedLdapEnv);
|
||||
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
|
@ -77,7 +78,7 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
|
|||
DirContext ctx = new InitialDirContext(ldapEnv);
|
||||
SearchControls searchCtls = new SearchControls();
|
||||
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
searchCtls.setReturningAttributes( new String[0] );
|
||||
searchCtls.setReturningAttributes( new String[] {} );
|
||||
|
||||
String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))";
|
||||
NamingEnumeration<SearchResult> results = ctx.search(userSearchDN, searchFilter, new Object[]{ userPrincipal }, searchCtls);
|
||||
|
@ -87,8 +88,7 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
|
|||
String name = entry.getNameInNamespace();
|
||||
|
||||
if (!results.hasMore()) {
|
||||
//isFindGroupsByAttribute=true, group subtree search=false, groupSubtreeDN=null
|
||||
return new LdapConnection(ctx, name, true, false, null);
|
||||
return new ActiveDirectoryConnection(ctx, name, userSearchDN);
|
||||
}
|
||||
throw new LdapException("Search for user [" + userName + "] by principle name yielded multiple results");
|
||||
}
|
||||
|
@ -107,4 +107,5 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
|
|||
String buildDnFromDomain(String domain) {
|
||||
return "DC=" + domain.replace(".", ",DC=");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.logging.ESLogger;
|
||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates jndi/ldap functionality into one authenticated connection. The constructor is package scoped, assuming
|
||||
* instances of this connection will be produced by the LdapConnectionFactory.bind() methods.
|
||||
*
|
||||
* A standard looking usage pattern could look like this:
|
||||
<pre>
|
||||
try (LdapConnection session = ldapFac.bindXXX(...);
|
||||
...do stuff with the session
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
public class GenericLdapConnection implements LdapConnection {
|
||||
|
||||
private static final ESLogger logger = ESLoggerFactory.getLogger(GenericLdapConnection.class.getName());
|
||||
private final String bindDn;
|
||||
protected final DirContext ldapContext;
|
||||
|
||||
private final String groupSearchDN;
|
||||
private final boolean isGroupSubTreeSearch;
|
||||
private final boolean isFindGroupsByAttribute;
|
||||
protected final String groupAttribute = "memberOf";
|
||||
|
||||
/**
|
||||
* This object is intended to be constructed by the LdapConnectionFactory
|
||||
*/
|
||||
GenericLdapConnection(DirContext ctx, String boundName, boolean isFindGroupsByAttribute, boolean isGroupSubTreeSearch, String groupSearchDN) {
|
||||
this.ldapContext = ctx;
|
||||
this.bindDn = boundName;
|
||||
this.isGroupSubTreeSearch = isGroupSubTreeSearch;
|
||||
this.groupSearchDN = groupSearchDN;
|
||||
this.isFindGroupsByAttribute = isFindGroupsByAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP connections should be closed to clean up resources. However, the jndi contexts have the finalize
|
||||
* implemented properly so that it will clean up on garbage collection.
|
||||
*/
|
||||
@Override
|
||||
public void close(){
|
||||
try {
|
||||
ldapContext.close();
|
||||
} catch (NamingException e) {
|
||||
throw new SecurityException("Could not close the LDAP connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups that the user is a member of
|
||||
* @return List of group membership
|
||||
*/
|
||||
@Override
|
||||
public List<String> getGroups(){
|
||||
List<String> groups = isFindGroupsByAttribute ? getGroupsFromUserAttrs(bindDn) : getGroupsFromSearch(bindDn);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found these groups [{}] for userDN [{}]", groups, this.bindDn);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups of a user by doing a search. This could be abstracted out into a strategy class or through
|
||||
* an inherited class (with getGroups as the template method).
|
||||
* @param userDn user fully distinguished name to fetch group membership for
|
||||
* @return fully distinguished names of the roles
|
||||
*/
|
||||
List<String> getGroupsFromSearch(String userDn){
|
||||
List<String> groups = new LinkedList<>();
|
||||
SearchControls search = new SearchControls();
|
||||
search.setReturningAttributes( new String[0] );
|
||||
search.setSearchScope( this.isGroupSubTreeSearch ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
|
||||
|
||||
//This could be made could be made configurable but it should cover all cases
|
||||
String filter = "(&" +
|
||||
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group)) " +
|
||||
"(|(uniqueMember={0})(member={0})))";
|
||||
|
||||
try {
|
||||
NamingEnumeration<SearchResult> results = ldapContext.search(
|
||||
groupSearchDN, filter, new Object[]{userDn}, search);
|
||||
while (results.hasMoreElements()){
|
||||
groups.add(results.next().getNameInNamespace());
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not search for an LDAP group for user [" + userDn + "]", e);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups from the user attributes (if supported). This method could later be abstracted out
|
||||
* into a strategy class
|
||||
* @param userDn User fully distinguished name to fetch group membership from
|
||||
* @return list of groups the user is a member of.
|
||||
*/
|
||||
List<String> getGroupsFromUserAttrs(String userDn) {
|
||||
List<String> groupDns = new LinkedList<>();
|
||||
try {
|
||||
Attributes results = ldapContext.getAttributes(userDn, new String[]{groupAttribute});
|
||||
for(NamingEnumeration ae = results.getAll(); ae.hasMore();) {
|
||||
Attribute attr = (Attribute)ae.next();
|
||||
for (NamingEnumeration attrEnum = attr.getAll(); attrEnum.hasMore();) {
|
||||
Object val = attrEnum.next();
|
||||
if (val instanceof String) {
|
||||
String stringVal = (String) val;
|
||||
groupDns.add(stringVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not look up group attributes for user [" + userDn + "]", e);
|
||||
}
|
||||
return groupDns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches common user attributes from the user. Its a good way to ensure a connection works.
|
||||
*/
|
||||
public Map<String,String[]> getUserAttrs(String userDn) {
|
||||
Map <String, String[]>userAttrs = new HashMap<>();
|
||||
try {
|
||||
Attributes results = ldapContext.getAttributes(userDn, new String[]{"uid", "memberOf", "isMemberOf"});
|
||||
for(NamingEnumeration ae = results.getAll(); ae.hasMore();) {
|
||||
Attribute attr = (Attribute)ae.next();
|
||||
LinkedList<String> attrList = new LinkedList<>();
|
||||
for (NamingEnumeration attrEnum = attr.getAll(); attrEnum.hasMore();) {
|
||||
Object val = attrEnum.next();
|
||||
if (val instanceof String) {
|
||||
String stringVal = (String) val;
|
||||
attrList.add(stringVal);
|
||||
}
|
||||
}
|
||||
String[] attrArray = attrList.toArray(new String[attrList.size()]);
|
||||
userAttrs.put(attr.getID(), attrArray);
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not look up attributes for user [" + userDn + "]", e);
|
||||
}
|
||||
return userAttrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticatedUserDn() {
|
||||
return bindDn;
|
||||
}
|
||||
}
|
|
@ -5,157 +5,16 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.authc.ldap;
|
||||
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.*;
|
||||
import java.io.Closeable;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates jndi/ldap functionality into one authenticated connection. The constructor is package scoped, assuming
|
||||
* instances of this connection will be produced by the LdapConnectionFactory.bind() methods.
|
||||
*
|
||||
* A standard looking usage pattern could look like this:
|
||||
<pre>
|
||||
try (LdapConnection session = ldapFac.bindXXX(...);
|
||||
...do stuff with the session
|
||||
}
|
||||
</pre>
|
||||
* Represents a LDAP connection with an authenticated/bound user that needs closing.
|
||||
*/
|
||||
public class LdapConnection implements Closeable {
|
||||
public interface LdapConnection extends Closeable {
|
||||
void close();
|
||||
|
||||
private static final ESLogger logger = ESLoggerFactory.getLogger(LdapConnection.class.getName());
|
||||
private final String bindDn;
|
||||
private final DirContext ldapContext;
|
||||
List<String> getGroups();
|
||||
|
||||
private final String groupSearchDN;
|
||||
private final boolean isGroupSubTreeSearch;
|
||||
private final boolean isFindGroupsByAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* This object is intended to be constructed by the LdapConnectionFactory
|
||||
*/
|
||||
LdapConnection(DirContext ctx, String boundName, boolean isFindGroupsByAttribute, boolean isGroupSubTreeSearch, String groupSearchDN) {
|
||||
this.ldapContext = ctx;
|
||||
this.bindDn = boundName;
|
||||
this.isGroupSubTreeSearch = isGroupSubTreeSearch;
|
||||
this.groupSearchDN = groupSearchDN;
|
||||
this.isFindGroupsByAttribute = isFindGroupsByAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP connections should be closed to clean up resources. However, the jndi contexts have the finalize
|
||||
* implemented properly so that it will clean up on garbage collection.
|
||||
*/
|
||||
public void close(){
|
||||
try {
|
||||
ldapContext.close();
|
||||
} catch (NamingException e) {
|
||||
throw new SecurityException("Could not close the LDAP connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups that the user is a member of
|
||||
* @return List of group membership
|
||||
*/
|
||||
public List<String> getGroups(){
|
||||
List<String> groups = isFindGroupsByAttribute ? getGroupsFromUserAttrs(bindDn) : getGroupsFromSearch(bindDn);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found these groups [{}] for userDN [{}]", groups, this.bindDn);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups of a user by doing a search. This could be abstracted out into a strategy class or through
|
||||
* an inherited class (with getGroups as the template method).
|
||||
* @param userDn user fully distinguished name to fetch group membership for
|
||||
* @return fully distinguished names of the roles
|
||||
*/
|
||||
List<String> getGroupsFromSearch(String userDn){
|
||||
List<String> groups = new LinkedList<>();
|
||||
SearchControls search = new SearchControls();
|
||||
search.setReturningAttributes( new String[0] );
|
||||
search.setSearchScope( this.isGroupSubTreeSearch ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
|
||||
|
||||
//This could be made could be made configurable but it should cover all cases
|
||||
String filter = "(&" +
|
||||
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group)) " +
|
||||
"(|(uniqueMember={0})(member={0})))";
|
||||
|
||||
try {
|
||||
NamingEnumeration<SearchResult> results = ldapContext.search(
|
||||
groupSearchDN, filter, new Object[]{userDn}, search);
|
||||
while (results.hasMoreElements()){
|
||||
groups.add(results.next().getNameInNamespace());
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not search for an LDAP group for user [" + userDn + "]", e);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the groups from the user attributes (if supported). This method could later be abstracted out
|
||||
* into a strategy class
|
||||
* @param userDn User fully distinguished name to fetch group membership from
|
||||
* @return list of groups the user is a member of.
|
||||
*/
|
||||
List<String> getGroupsFromUserAttrs(String userDn) {
|
||||
List<String> groupDns = new LinkedList<>();
|
||||
try {
|
||||
Attributes results = ldapContext.getAttributes(userDn, new String[]{"memberOf", "isMemberOf"});
|
||||
for(NamingEnumeration ae = results.getAll(); ae.hasMore();) {
|
||||
Attribute attr = (Attribute)ae.next();
|
||||
for (NamingEnumeration attrEnum = attr.getAll(); attrEnum.hasMore();) {
|
||||
Object val = attrEnum.next();
|
||||
if (val instanceof String) {
|
||||
String stringVal = (String) val;
|
||||
groupDns.add(stringVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not look up group attributes for user [" + userDn + "]", e);
|
||||
}
|
||||
return groupDns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches common user attributes from the user. Its a good way to ensure a connection works.
|
||||
*/
|
||||
public Map<String,String[]> getUserAttrs(String userDn) {
|
||||
Map <String, String[]>userAttrs = new HashMap<>();
|
||||
try {
|
||||
Attributes results = ldapContext.getAttributes(userDn, new String[]{"uid", "memberOf", "isMemberOf"});
|
||||
for(NamingEnumeration ae = results.getAll(); ae.hasMore();) {
|
||||
Attribute attr = (Attribute)ae.next();
|
||||
LinkedList<String> attrList = new LinkedList<>();
|
||||
for (NamingEnumeration attrEnum = attr.getAll(); attrEnum.hasMore();) {
|
||||
Object val = attrEnum.next();
|
||||
if (val instanceof String) {
|
||||
String stringVal = (String) val;
|
||||
attrList.add(stringVal);
|
||||
}
|
||||
}
|
||||
String[] attrArray = attrList.toArray(new String[attrList.size()]);
|
||||
userAttrs.put(attr.getID(), attrArray);
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new LdapException("Could not look up attributes for user [" + userDn + "]", e);
|
||||
}
|
||||
return userAttrs;
|
||||
}
|
||||
|
||||
public String getAuthenticatedUserDn() {
|
||||
return bindDn;
|
||||
}
|
||||
String getAuthenticatedUserDn();
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class StandardLdapConnectionFactory extends AbstractComponent implements
|
|||
* @return authenticated exception
|
||||
*/
|
||||
@Override
|
||||
public LdapConnection bind(String username, SecuredString password) {
|
||||
public GenericLdapConnection bind(String username, SecuredString password) {
|
||||
//SASL, MD5, etc. all options here stink, we really need to go over ssl + simple authentication
|
||||
Hashtable<String, Serializable> ldapEnv = new Hashtable<>(this.sharedLdapEnv);
|
||||
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
|
@ -86,7 +86,7 @@ public class StandardLdapConnectionFactory extends AbstractComponent implements
|
|||
DirContext ctx = new InitialDirContext(ldapEnv);
|
||||
|
||||
//return the first good connection
|
||||
return new LdapConnection(ctx, dn, findGroupsByAttribute, groupSubTreeSearch, groupSearchDN);
|
||||
return new GenericLdapConnection(ctx, dn, findGroupsByAttribute, groupSubTreeSearch, groupSearchDN);
|
||||
|
||||
} catch (NamingException e) {
|
||||
logger.warn("Failed ldap authentication with user template [{}], dn [{}]", e, template, dn );
|
||||
|
|
|
@ -52,14 +52,18 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
|
|||
try (LdapConnection ldap = connectionFactory.bind(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
String userDN = ldap.getAuthenticatedUserDn();
|
||||
|
||||
List<String> groups = ldap.getGroupsFromUserAttrs(userDN);
|
||||
List<String> groups = ldap.getGroups();
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Geniuses"),
|
||||
containsString("Billionaire"),
|
||||
containsString("Playboy"),
|
||||
containsString("Philanthropists"),
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD")));
|
||||
containsString("SHIELD"),
|
||||
containsString("Users"),
|
||||
containsString("Domain Users"),
|
||||
containsString("Supers")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,13 +90,17 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
|
|||
try (LdapConnection ldap = connectionFactory.bind(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
String userDN = ldap.getAuthenticatedUserDn();
|
||||
|
||||
List<String> groups = ldap.getGroupsFromUserAttrs(userDN);
|
||||
List<String> groups = ldap.getGroups();
|
||||
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
containsString("Geniuses"),
|
||||
containsString("Philanthropists")));
|
||||
containsString("Philanthropists"),
|
||||
containsString("Users"),
|
||||
containsString("Domain Users"),
|
||||
containsString("Supers")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +113,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
|
|||
LdapTest.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, isSubTreeSearch));
|
||||
|
||||
String user = "Bruce Banner";
|
||||
try (LdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD))) {
|
||||
try (GenericLdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD))) {
|
||||
List<String> groups = ldap.getGroupsFromUserAttrs(ldap.getAuthenticatedUserDn());
|
||||
List<String> groups2 = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public class LdapConnectionTests extends LdapTest {
|
|||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
||||
try (LdapConnection ldap = connectionFactory.bind(user, userPass)) {
|
||||
try (GenericLdapConnection ldap = connectionFactory.bind(user, userPass)) {
|
||||
Map<String, String[]> attrs = ldap.getUserAttrs(ldap.getAuthenticatedUserDn());
|
||||
|
||||
assertThat(attrs, hasKey("uid"));
|
||||
|
@ -55,7 +55,7 @@ public class LdapConnectionTests extends LdapTest {
|
|||
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
try (LdapConnection ldapConnection = ldapFac.bind(user, userPass)) {
|
||||
try (GenericLdapConnection ldapConnection = ldapFac.bind(user, userPass)) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class LdapConnectionTests extends LdapTest {
|
|||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
||||
try (LdapConnection ldap = ldapFac.bind(user, userPass)) {
|
||||
try (GenericLdapConnection ldap = ldapFac.bind(user, userPass)) {
|
||||
List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
|
||||
assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public class LdapConnectionTests extends LdapTest {
|
|||
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
try (LdapConnection ldap = ldapFac.bind(user, SecuredStringTests.build("pass"))) {
|
||||
try (GenericLdapConnection ldap = ldapFac.bind(user, SecuredStringTests.build("pass"))) {
|
||||
List<String> groups = ldap.getGroupsFromSearch(ldap.getAuthenticatedUserDn());
|
||||
assertThat(groups, contains("cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas"));
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
|||
|
||||
String[] users = new String[]{"blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor"};
|
||||
for(String user: users) {
|
||||
LdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD));
|
||||
GenericLdapConnection ldap = connectionFactory.bind(user, SecuredStringTests.build(PASSWORD));
|
||||
assertThat(ldap.getGroups(), hasItem(containsString("Avengers")));
|
||||
ldap.close();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue