Thursday, October 29, 2009

OMG - It Just Works!? or HowTo talk to LDAP from Grails

I was shocked (what, again!?) to find talking to LDAP from Grails so easy; typical Java/LDAP code looks like this, where the code marked as bold actually performs tasks related to the business purpose of the method:
package com.example.dao;
public class TraditionalPersonDaoImpl implements PersonDao {
  public List getAllPersonNames() {
     Hashtable env = new Hashtable();
     env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
     env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=example,dc=com");

     DirContext ctx;
     try {
        ctx = new InitialDirContext(env);
     } catch (NamingException e) {
        throw new RuntimeException(e);
     }

     LinkedList list = new LinkedList();
     NamingEnumeration results = null;
     try {
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        results = ctx.search("", "(objectclass=person)", controls);

        while (results.hasMore()) {
           SearchResult searchResult = (SearchResult) results.next();
           Attributes attributes = searchResult.getAttributes();
           Attribute attr = attributes.get("cn");
           String cn = (String) attr.get();
           list.add(cn);
        }
     } catch (NameNotFoundException e) {
        // The base context was not found.
        // Just clean up and exit.
     } catch (NamingException e) {
        throw new RuntimeException(e);
     } finally {
        if (results != null) {
           try {
              results.close();
           } catch (Exception e) {
              // Never mind this.
           }
        }
        if (ctx != null) {
           try {
              ctx.close();
           } catch (Exception e) {
              // Never mind this.
           }
        }
     }
     return list;
  }
}
That's a lot of noise around actually_doing_something, not to mention the (not shown) behind-the-scenes config'ing necessary to make it work.
Here are the steps in Grails:
  1. From the command-line inside your grails application do:

    1. grails install-plugin ldap

    2. the above command installs the Grails LDAP plugin which is based on the GroovyLDAPObject project which is based upon the Spring LDAP library


  2. Add something like this to the standard Grails file grails-app/conf/Config.groovy


    1. ldap {
         directories {
             directory1 {
                 defaultDirectory = true
                 url = "ldap://ldap-host.domain"
                 userDn = "cn=someLDAPAdminID,ou=myOU,o=myOrg"
                 password = "someLDAPAdminIDPassword"
                 searchControls {
                     countLimit = 40
                     timeLimit = 600
                     searchScope = "subtree"
                 }
             }
         }
      
         schemas = [
             GldapoSchemaClassForUser
         ]
      
      }

  3. Create a Groovy class to represent an LDAP entity e.g. in this case a user of our application i.e. utils/GldapoSchemaClassForUser.groovy

    1. import gldapo.schema.annotation.GldapoNamingAttribute
      import gldapo.schema.annotation.GldapoSynonymFor
      import gldapo.schema.annotation.GldapoSchemaFilter
      
      @GldapoSchemaFilter("(objectclass=person)")
      class GldapoSchemaClassForUser {
        
         @GldapoSynonymFor("cn")
         String name
        
         @GldapoSynonymFor("mail")
         String email
        
         @GldapoSynonymFor("uid")
         String username
        
         @GldapoSynonymFor("fullname")
         String fullName
      }


    2. the schema filter annotation forces searches to only return LDAP entities of a particular type

    3. the synonym annotation allows you to map an LDAP entity's attributes to Groovy object attributes of your own choice

    4. only the cn attribute needs to be in this class


Sorry, that's all there is to it.

To list a bunch of LDAP persons (e.g. whose cn start with mi) you can use code like this in a Grails controller or service class:
List matches = GldapoSchemaClassForUser.findAll(
     directory: "directory1",
     base: "ou=someOU,o=someOrg",
     filter: "(cn=mi*)"
 )

I intend to combine this LDAP plugin usage with Grails' jSecurity plugin to authenticate users to our Grails apps, perhaps combining it all into a plugin for our developers to use like so:
grails install-plugin ourShop_authc
Note: the LDAP configuration can also be done programmatically so the LDAP Admin credentials could be pulled from a DB (e.g. in conf/Bootstrap.groovy) if desired.

1 comment:

Leinad said...

Gldapo in ldap plugin is NOT FUNCTIONAL UNDER JAVA 7! :-(