Pages

LDAP Authentication & Authorization Dissected and Digested

LDAP is one of those things that I’ve integrated with a few times but never put enough energy into to really get the details or understand it much.  There’s always been someone I can bounce questions off of and thankfully those people were available again as I started working out the details of performing LDAP authentication.
The steps below are general enough to be used by anyone and will hopefully shed some light into the steps performed in LDAP authentication.  The process below also includes some steps for authorization.

Authentication

1.  Get a connection to the LDAP server.
With the host and port for the LDAP server, create a connection to it.  This can be  a simple direct connection or a pooled connection.  If more than a basic test, it is best to use a pooled connection.  Log and fail if a connection cannot be created.

2.  Bind as the application user.
Bind the connection to the application user.  This user should have enough permissions to search the area of LDAP where users are located.  This user may also have permissions to search for things outside of the users area (groups, authorization).  Log and fail if the application user cannot bind.
3.  Search for the DN (distinguished name) of the user to be authenticated.
This is where we verify the username is valid.  This does not authenticate the user but simply makes sure the requested username exists in the system.  Log and fail if the user’s DN is not found.
4.  Bind as user to be authenticated using DN from step 3.
Now for the moment of truth.  Bind to the connection using the DN found in step 3 and the password supplied by the user.  Log and fail if unable to bind using the user’s DN and password.

Authorization

5.  Re-bind as application user.
To check the authorization of a user, we need to read attributes from the user’s account. To do this, we need to re-bind as the application user.
6.  Search for user and require attributes.
A filter is used to search for the user like was done in step 3 but we’ll add an extra check to the query to look for the attributes that show we’re authorized.

Example Code

The code shown here is using the JLDAP library from Novell.  Interesting includes are noted at the top. The utility class used for escaping the search filter can be found in the Sakai Project’s Nakamura codebase.
001import com.novell.ldap.LDAPAttribute;
002import com.novell.ldap.LDAPConnection;
003import com.novell.ldap.LDAPEntry;
004import com.novell.ldap.LDAPException;
005import com.novell.ldap.LDAPSearchResults;
006// ...
007String baseDn = "ou=People,o=MyOrg";
008String userFilter = "uid = {}";
009String authzFilter = "authzAttr=special:Entitlement:value";
010// ...
011public boolean authenticate(Credentials credentials) throws RepositoryException {
012    boolean auth = false;
013    if (credentials instanceof SimpleCredentials) {
014      // get application user credentials
015      String appUser = connMgr.getConfig().getLdapUser();
016      String appPass = connMgr.getConfig().getLdapPassword();
017 
018      // get user credentials
019      SimpleCredentials sc = (SimpleCredentials) credentials;
020 
021      long timeStart = System.currentTimeMillis();
022 
023      String userDn = LdapUtil.escapeLDAPSearchFilter(userFilter.replace("{}",
024          sc.getUserID()));
025      String userPass = new String(sc.getPassword());
026 
027      LDAPConnection conn = null;
028      try {
029        // 1) Get a connection to the server
030        try {
031          conn = connMgr.getConnection();
032          log.debug("Connected to LDAP server");
033        } catch (LDAPException e) {
034          throw new IllegalStateException("Unable to connect to LDAP server ["
035              + connMgr.getConfig().getLdapHost() + "]");
036        }
037 
038        // 2) Bind as app user
039        try {
040          conn.bind(LDAPConnection.LDAP_V3, appUser, appPass.getBytes(UTF8));
041          log.debug("Bound as application user");
042        } catch (LDAPException e) {
043          throw new IllegalArgumentException("Can't bind application user [" + appUser
044              + "]", e);
045        }
046 
047        // 3) Search for username (not authz).
048        // If search fails, log/report invalid username or password.
049        LDAPSearchResults results = conn.search(baseDn, LDAPConnection.SCOPE_SUB, userDn,
050            null, true);
051        if (results.hasMore()) {
052          log.debug("Found user via search");
053        } else {
054          throw new IllegalArgumentException("Can't find user [" + userDn + "]");
055        }
056 
057        // 4) Bind as user.
058        // If bind fails, log/report invalid username or password.
059 
060        // value is set below. define here for use in authz check.
061        String userEntryDn = null;
062        try {
063          LDAPEntry userEntry = results.next();
064          LDAPAttribute objectClass = userEntry.getAttribute("objectClass");
065 
066          if ("aliasObject".equals(objectClass.getStringValue())) {
067            LDAPAttribute aliasDN = userEntry.getAttribute("aliasedObjectName");
068            userEntryDn = aliasDN.getStringValue();
069          } else {
070            userEntryDn = userEntry.getDN();
071          }
072 
073          conn.bind(LDAPConnection.LDAP_V3, userEntryDn, userPass.getBytes(UTF8));
074          log.debug("Bound as user");
075        } catch (LDAPException e) {
076          log.warn("Can't bind user [{}]", userDn);
077          throw e;
078        }
079 
080        if (authzFilter.length() > 0) {
081          // 5) Return to app user
082          try {
083            conn.bind(LDAPConnection.LDAP_V3, appUser, appPass.getBytes(UTF8));
084            log.debug("Rebound as application user");
085          } catch (LDAPException e) {
086            throw new IllegalArgumentException("Can't bind application user [" + appUser
087                + "]");
088          }
089 
090          // 6) Search user DN with authz filter
091          // If search fails, log/report that user is not authorized
092          String userAuthzFilter = "(&(" + userEntryDn + ")(" + authzFilter + "))";
093          results = conn.search(baseDn, LDAPConnection.SCOPE_SUB, userAuthzFilter, null,
094              true);
095          if (results.hasMore()) {
096            log.debug("Found user + authz filter via search");
097          } else {
098            throw new IllegalArgumentException("User not authorized [" + userDn + "]");
099          }
100        }
101 
102        // FINALLY!
103        auth = true;
104        log.info("User [{}] authenticated with LDAP in {}ms", userDn,
105            System.currentTimeMillis() - timeStart);
106      } catch (Exception e) {
107        log.warn(e.getMessage(), e);
108      } finally {
109        connMgr.returnConnection(conn);
110      }
111    }
112    return auth;
113  }

More Here


Courtesy:http://thecarlhall.wordpress.com/2011/01/04/ldap-authentication-authorization-dissected-and-digested/

0 comments:

Post a Comment