Sunday, June 13, 2010

Active Directory Authorization with Java

I have a situation where I need to be able to have sub groupings of users in Active directory to manage who can see particular pieces of information. It turns out this is easy, but unintuitive. An important detail is to realize that groups can be put inside into other groups and you can use the "member" and "memberOf" attributes to determine who is in which group. So if you have an OU in Active directory called "OU=web,DC=mainguy,DC=org" and you create a group with a name of "CN=Germany National Sales,OU=web,DC=mainguy,DC=org". From here in Active directory you create any number of subgroups and put them in the parent group (under the same OU in our example, but that's not necessary).

At this point, you can dump users into any of the groups and you can get segregate users into nested structures. With a little creativity you can use recursion to have deeper nesting (not necessarily a good thing) as well as a "deny/allow" capability (perhaps based on ou).

In any event, here's the code that can get you started.


package org.mainguy;

import java.util.Hashtable;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

class FastBindConnectionControl implements Control {
 public byte[] getEncodedValue() {
  return null;
 }

 public String getID() {
  return "1.2.840.113556.1.4.1781";
 }

 public boolean isCritical() {
  return true;
 }
}

public class LDAPBinder {
 public Hashtable env = null;
 public LdapContext ctx = null;
 public Control[] connCtls = null;

 public LDAPBinder(String ldapurl) {
  env = new Hashtable();
  env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.ldap.LdapCtxFactory");
  env.put(Context.SECURITY_AUTHENTICATION, "simple");
  env.put(Context.PROVIDER_URL, ldapurl);

  connCtls = new Control[] { new FastBindConnectionControl() };

  try {
   ctx = new InitialLdapContext(env, connCtls);
        
  } catch (NamingException e) {
   System.out.println("Naming exception " + e);
  }
 }

 public boolean authenticate(String username, String password) {
  try {
   ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, username);
   ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
   ctx.reconnect(connCtls);
   System.out.println(username + " is authenticated");
      return true;
  }

  catch (AuthenticationException e) {
   System.out.println(username + " is not authenticated");
   return false;
  } catch (NamingException e) {
   System.out.println(username + " is not authenticated");
   return false;
  }
 }

 public void finito() {
  try {
   ctx.close();
   System.out.println("Context is closed");
  } catch (NamingException e) {
   System.out.println("Context close failure " + e);
  }
 }

 public void getMembership(String name) {
  String[] returns = {"member"};
  SearchControls sc = new SearchControls();
  sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
  sc.setReturningAttributes(returns);
  try {
   NamingEnumeration ne = ctx.search("OU=web,DC=mainguy,DC=org","memberOf="+name,sc);
   
   while (ne.hasMoreElements()) {
    SearchResult sr = (SearchResult)ne.next();
    System.out.println("+" + sr.getName());
    
    Attributes attr = sr.getAttributes();
    System.out.println("--" + attr.get("member").size());

    NamingEnumeration allUsers = attr.get("member").getAll();
    
    while (allUsers.hasMoreElements()) {
     String value = (String)allUsers.next();
     System.out.println("---"+value);
     
    }
        
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 /**
  * @param args
  */
 public static void main(String[] args) {
  LDAPBinder binder = new LDAPBinder("ldap://173.203.66.30:389");
  binder.authenticate("maxplanck@mainguy.org", "supersecret");
  binder.getMembership("CN=Germany National Sales,OU=web,DC=mainguy,DC=org");

  binder.finito();
 }

}

1 comment:

Tae Youn Kim said...

Need a blank password check.