4

I am using JNDI to connect to the LDAP active directory, and I want to search for users where the name contains the search string, so my search method is as follows:

public static List<LDAPUser> searchContactsByName(
        ExtendedDirContext extendedDirContext, String name) {

    try {

        LdapContext ldapContext = extendedDirContext.getLdapContext();
        String searchBaseStr = extendedDirContext.getSearchBase();

        String sortKey = LDAPAttributes.NAME;
        ldapContext.setRequestControls(new Control[] { new SortControl(
                sortKey, Control.CRITICAL) });

        SearchControls searchCtls = new SearchControls();
        searchCtls.setTimeLimit(1000 * 10);

        String returnedAtts[] = { LDAPAttributes.USER_NAME,
                LDAPAttributes.NAME };
        searchCtls.setReturningAttributes(returnedAtts);

        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        String searchFilter = "(&(ObjectCategory=person)(cn=*" + name
                + "*))";

        NamingEnumeration<SearchResult> results = ldapContext.search(
                searchBaseStr, searchFilter, searchCtls);

        List<LDAPUser> users = new ArrayList<LDAPUser>(0);
        while (results.hasMoreElements()) {
            SearchResult sr = (SearchResult) results.next();
            Attributes attrs = sr.getAttributes();
            LDAPUser user = new LDAPUser();
            user.setName(attrs.get(LDAPAttributes.NAME).toString()
                    .replace("cn: ", ""));
            user.setUserName(attrs.get(LDAPAttributes.USER_NAME).toString()
                    .replace("sAMAccountName: ", ""));
            users.add(user);
        }

        return users;

    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

and here is how I am making the connection to LDAP:

public static ExtendedDirContext connectToLdap(MessageSource messageSource) {

    try {
        log.debug("connectToLdap");
        String providerUrl = messageSource.getMessage("provider.url", null,
                null);
        String securityPrincipal = messageSource.getMessage(
                "security.principal", null, null);
        String securityCredentials = messageSource.getMessage(
                "security.credentials", null, null);
        String searchBase = messageSource.getMessage("search.base", null,
                null);
        boolean ssl = Boolean.parseBoolean(messageSource.getMessage("ssl",
                null, null));
        LdapContext ldapContext;

        Hashtable<String, String> ldapEnv = new Hashtable<String, String>(
                11);
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, providerUrl);
        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        ldapEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
        ldapEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials);
        if (ssl)
            ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
        // To get rid of the PartialResultException when using Active
        // Directory
        ldapEnv.put(Context.REFERRAL, "follow");
        ldapContext = new InitialLdapContext(ldapEnv, null);
        ExtendedDirContext extendedDirContext = new ExtendedDirContext();
        extendedDirContext.setLdapContext(ldapContext);
        extendedDirContext.setSearchBase(searchBase);
        log.debug("success connection to ldap");
        return extendedDirContext;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }

}

The LDAP credentials are as follows:

provider.url=ldap://dc.fabrikam.com:389
security.principal=CN=administrator,CN=Users,DC=fabrikam,DC=com
security.credentials=password
search.base=dc=fabrikam,dc=com

Why does the search take so much time to retrieve the data? Is there any change that I can do to make the search faster, since I have only 285 contacts in the AD?

Dave Cassel
  • 8,352
  • 20
  • 38
Mahmoud Saleh
  • 33,303
  • 119
  • 337
  • 498
  • 1
    You are performing a prefix wildcard search, what do you expect? This runs in `O(n)` time. – Michael-O Jan 03 '13 at 10:56
  • @Michael-O, but i don't have too many users in the AD, only 285. – Mahmoud Saleh Jan 03 '13 at 10:58
  • omit the first `*` and see if it changes. – Michael-O Jan 03 '13 at 11:15
  • @Michael-O same behaviour, more than 90 seconds to get search results, please see the post update of how i am making the connection to ldap. – Mahmoud Saleh Jan 03 '13 at 11:20
  • You may try this and see whether you get the same results: http://www.ldapbrowser.com/info_softerra-ldap-browser.htm – Michael-O Jan 03 '13 at 11:23
  • 5
    I've found that changing `ldapEnv.put(Context.REFERRAL, "follow");` to `ldapEnv.put(Context.REFERRAL, "ignore");` makes searches pretty quick – Anya Shenanigans Jan 03 '13 at 11:47
  • Does the query run quickly enough if you omit the `SortControl`? I suspect the combination of a wildcard search (and note that searches that prefix the query with `*` will ignore all indexing) and sorting. See [this document](http://msdn.microsoft.com/en-gb/library/windows/desktop/aa746491(v=vs.85).aspx) for Microsoft's recommendations on using the sort control. – ig0774 Jan 03 '13 at 14:11
  • The comment about referrals is telling. My guess is that you're chasing a referral that isn't working out...a firewall in the way, domain that doesn't exist anymore, etc. And the timeout in your client API is ~90 seconds. A network sniff would confirm this. – Eric Fleischman Jan 03 '13 at 17:37
  • @Petesh, you are correct, search works fine now. – Mahmoud Saleh Jan 08 '13 at 07:43

3 Answers3

13

Solution was to change ldapEnv.put(Context.REFERRAL, "follow"); to ldapEnv.put(Context.REFERRAL, "ignore");

Mahmoud Saleh
  • 33,303
  • 119
  • 337
  • 498
3

Your filter:

"(&(ObjectCategory=person)(cn=*" + name + "*))"

May be an issue.

I would recommend that you download a known LDAP utility (Apache Directory Studio Browser as an example) and try different search filters until you find one that works.

To Start, try

"(&(ObjectCategory=person)(cn= + name ))"
mpromonet
  • 11,326
  • 43
  • 62
  • 91
jwilleke
  • 10,467
  • 1
  • 30
  • 51
2

You're right,

ldapEnv.put(Context.REFERRAL, "ignore") 

didn't get exception about connection timed out. But when I first try I get a partialexception. After I changed my LDAP configuration port from 389 to 3268 I didn't get any exception, build successfully. 3268 port about global catalog of LDAP. For example Outlook clients query the global catalog to locate Address Book information. You can try global catalog if you get an exception referral type setting.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
mertaksu
  • 535
  • 1
  • 9
  • 26