Thursday, May 20, 2010

Accessing MS Active Directory using LDAP

In this example, we'll implement a simple client that performs two simple lookups in Microsoft Active Directory to retrieve groups and users through the LDAP interface. The LDAP interface can be accessed using the standard JNDI API.

The Microsoft implementation of the LDAP interface is limited to 1000 query results. So to handle more than 1000 results, we have to maintain a cookie that stores the current position of retrieved results and pass the cookie back to the server to continue the query from the cookie stored position. This is called a paged query.

We first start by creating a LdapContext object, which we'll use to access Active Directory through the LDAP interface. The Context object is configured using a Hashtable.

Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "<user>");
env.put(Context.SECURITY_CREDENTIALS, "<password>");
env.put(Context.PROVIDER_URL, "ldap://<host>:389");
env.put(Context.REFERRAL, "follow");

// We want to use a connection pool to
// improve connection reuse and performance.
env.put("com.sun.jndi.ldap.connect.pool", "true");

// The maximum time in milliseconds we are going
// to wait for a pooled connection.
env.put("com.sun.jndi.ldap.connect.timeout", "300000");

Next, we need to specify how we are going to search the directory with a SearchControls object, from where we want to start the search with a search base string, and how we want to filter results.

SearchControls searchCtls = new SearchControls();
// We start our search from the search base.
// Be careful! The string needs to be escaped!
String searchBase = "dc=company,dc=com";

// There are different search scopes:
// - OBJECT_SCOPE, to search the named object
// - ONELEVEL_SCOPE, to search in only one level of the tree
// - SUBTREE_SCOPE, to search the entire subtree
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

// We only want groups, so we filter the search for
// objectClass=group. We use wildcards to find all groups with the
// string "<group>" in its name. If we want to find users, we can
// use: "(&(objectClass=person)(cn=*<username>*))".
String searchFilter = "(&(objectClass=group)(cn=*<group>*))";

// We want all results.
searchCtls.setCountLimit(0);

// We want to wait to get all results.
searchCtls.setTimeLimit(0);

// Active Directory limits our results, so we need multiple
// requests to retrieve all results. The cookie is used to
// save the current position.
byte[] cookie = null;

// We want 500 results per request.
ctx.setRequestControls(
new Control[] {
new PagedResultsControl(500, Control.CRITICAL)
});

// We only want to retrieve the "distinguishedName" attribute.
// You can specify other attributes/properties if you want here.
String returnedAtts[] = { "distinguishedName" };
searchCtls.setReturningAttributes(returnedAtts);

// The request loop starts here.
do {
// Start the search with our configuration.
NamingEnumeration<SearchResult> answer = ctx.search(
searchBase, searchFilter, searchCtls);

// Loop through the search results.
while (answer.hasMoreElements()) {
SearchResult sr = answer.next();
Attributes attr = sr.getAttributes();
Attribute a = attr.get("distinguishedName");
// Print our wanted attribute value.
System.out.println((String) a.get());
}

// Find the cookie in our response and save it.
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof
PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) controls[i];
cookie = prrc.getCookie();
}
}
}

// Use the cookie to configure our new request
// to start from the saved position in the cookie.
ctx.setRequestControls(new Control[] {
new PagedResultsControl(500,
cookie,
Control.CRITICAL) });
} while (cookie != null);

// We are done, so close the Context object.
ctx.close();

We can also print all attributes from an object using its fully distinguished name using the following code:

String dN = "CN=JohnDoe,OU=Users,DC=company,DC=com";
Attributes answer = ctx.getAttributes(dN);

for (NamingEnumeration<?> ae = answer.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
String attributeName = attr.getID();
for (NamingEnumeration<?> e = attr.getAll(); e.hasMore();) {
System.out.println(attributeName + "=" + e.next());
}
}

No comments:

Post a Comment