Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * JBoss, Home of Professional Open Source.
   * Copyright 2011, Red Hat Middleware LLC, and individual contributors
   * as indicated by the @author tags. See the copyright.txt file in the
   * distribution for a full listing of individual contributors. 
   *
   * This is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as
   * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.jboss.security.auth.callback;
 
 import java.util.Map;
 
 
 import  org.jboss.security.PicketBoxLogger;
 import  org.jboss.security.PicketBoxMessages;

A CallbackHandler using the LDAP to match the passed password.

There are two callbacks that can be passed to this handler.

  1. PasswordCallback: Passing this callback will get the password for the user. The returned password will not be in clear text. It will be in the hashed form the ldap server has stored.
  2. VerifyPasswordCallback Passing this callback with a value will make the handler to do a ldap bind to verify the user password.

The main method is #setConfiguration(Map) which takes in a map of String key/value pairs. The possible pairs are:

  1. passwordAttributeID : what is the name of the attribute where the password is stored. Default: userPassword
  2. bindDN : DN used to bind against the ldap server with read/write permissions for baseCtxDN.
  3. bindCredential : Password for the bindDN. This can be encrypted if the jaasSecurityDomain is specified.
  4. baseCtxDN : The fixed DN of the context to start the user search from.
  5. baseFilter: A search filter used to locate the context of the user to authenticate. The input username/userDN as provided by the NameCallback will be substituted into the filter anywhere a "{0}" expression is seen. This substitution behavior comes from the standard.
  6. searchTimeLimit : The timeout in milliseconds for the user/role searches. Defaults to 10000 (10 seconds).
  7. jaasSecurityDomain : The JMX ObjectName of the JaasSecurityDomain to use to decrypt the java.naming.security.principal. The encrypted form of the password is that returned by the JaasSecurityDomain#encrypt64(byte[]) method. The org.jboss.security.plugins.PBEUtils can also be used to generate the encrypted form.
  8. distinguishedNameAttribute : Used in ldap servers such as Active Directory where the ldap provider has a property (distinguishedName) to return the relative CN of the user. Default: distinguishedName

Example Usages:

  LdapCallbackHandler cbh = new LdapCallbackHandler();
  Map<String,String> map = new HashMap<String,String>();
  map.put("bindDN", "cn=Directory Manager");
  map.put("bindCredential", "password");
  map.put("baseFilter", "(uid={0})");
  map.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
  map.put("java.naming.provider.url", "ldap://localhost:10389");
  map.put("baseCtxDN", "ou=People,dc=jboss,dc=org");
  cbh.setConfiguration(map);
  NameCallback ncb = new NameCallback("Enter");
  ncb.setName("jduke");
  VerifyPasswordCallback vpc = new VerifyPasswordCallback();
  vpc.setValue("theduke");
  cbh.handle(new Callback[] {ncb,vpc} );
  assertTrue(vpc.isVerified());
 

Author(s):
Anil Saldhana
Since:
Nov 1, 2011
public class LdapCallbackHandler extends AbstractCallbackHandler implements CallbackHandler 
	private static final String PASSWORD_ATTRIBUTE_ID = "passwordAttributeID";
	private static final String BIND_DN = "bindDN";
	private static final String BIND_CREDENTIAL = "bindCredential";
	private static final String BASE_CTX_DN = "baseCtxDN";
	private static final String BASE_FILTER_OPT = "baseFilter"
	private static final String SEARCH_TIME_LIMIT_OPT = "searchTimeLimit"
	private static final String SECURITY_DOMAIN_OPT = "jaasSecurityDomain";
	   
	private static final String DISTINGUISHED_NAME_ATTRIBUTE_OPT = "distinguishedNameAttribute";
	protected String bindDN;
	protected String passwordAttributeID = "userPassword";
 
	protected int searchTimeLimit = 10000;
	// simple flag to indicate is the validatePassword method was called
	protected boolean isPasswordValidated = false;
	protected Map<String,Stringoptions = new HashMap<StringString>();
	{	
	}
	public void setConfiguration(Map<String,Stringconfig)
	{
		if(config != null)
		{
			.putAll(config);
		}
	}
	public void handle(Callback[] callbacksthrows IOException,
	{
		if( == null)
		{
			 = getUserName(callbacks);
		}
		for (int i = 0; i < callbacks.lengthi++)
		{
			Callback callback = callbacks[i];
			try 
			{
				this.handleCallBackcallback );
			catch (NamingException e
			{
				throw new IOException(e);
		}
	}

Handle a Callback

Parameters:
c callback
Throws:
UnsupportedCallbackException If the callback is not supported by this handler
NamingException
		if(c instanceof VerifyPasswordCallback)
		{
			return;
		}
		if(c instanceof PasswordCallback == false)
			return;
		PasswordCallback passwdCallback = (PasswordCallbackc;
		String bindDN = getBindDN();
		String bindCredential = getBindCredential(); 
		if(tmp != null && tmp.length() > 0)
		{
		}
		ClassLoader currentTCCL = SecurityActions.getContextClassLoader();
		try
		{
			if (currentTCCL != null)
				SecurityActions.setContextClassLoader(null);
			ctx = this.constructInitialLdapContext(bindDNbindCredential);
		}
		catch (NamingException e)
		{
			throw new RuntimeException(e);
		if (timeLimit != null)
		{
			try
			{
				 = Integer.parseInt(timeLimit);
			}
			{
			}
		}
		if( == 0)
			 = 10000;
		SearchControls constraints = new SearchControls();
		NamingEnumeration<SearchResultresults = null;
		Object[] filterArgs = {};
		try
		{
			if(baseDN == null)
				throw PicketBoxMessages.MESSAGES.invalidNullBaseContextDN();
			results = ctx.search(baseDNbaseFilterfilterArgsconstraints);
			if (results.hasMore() == false)
			{
				safeClose(results);
				throw PicketBoxMessages.MESSAGES.failedToFindBaseContextDN(baseDN);
			}
			SearchResult sr = results.next();
			String name = sr.getName();
			String userDN = null;
			if (sr.isRelative() == true)
				userDN = name + "," + baseDN;
			else
				throw PicketBoxMessages.MESSAGES.unableToFollowReferralForAuth(name);;
			safeClose(results);
			//Finished Authentication.  Lets look for the attributes
			filterArgs = new Object[]{userDN};
			results = ctx.search(userDNbaseFilterfilterArgsconstraints);
			try
			{
				while (results.hasMore())
				{
					sr = results.next();
					Attributes attributes = sr.getAttributes();
					NamingEnumeration<? extends javax.naming.directory.Attributene = attributes.getAll();
					while(ne != null && ne.hasMoreElements())
					{
						javax.naming.directory.Attribute ldapAtt = ne.next();
						{
							Object thePass = ldapAtt.get();
							setPasswordCallbackValue(thePasspasswdCallback);
						}
				}       
			}
			finally
			{
				safeClose(results);
				if (currentTCCL != null)
					SecurityActions.setContextClassLoader(currentTCCL);
			}            
		}
		catch(NamingException ne)
		{
			PicketBoxLogger.LOGGER.error(ne);
		}
	}
	protected void verifyPasswordVerifyPasswordCallback vpcthrows NamingException
	{
		String credential = vpc.getValue();
		ClassLoader currentTCCL = SecurityActions.getContextClassLoader();
		if (currentTCCL != null)
			SecurityActions.setContextClassLoader(null);
		String  baseDN = .get();
		String  baseFilter = .get();
		bindDNAuthentication(ctxcredentialbaseDNbaseFilter);
		vpc.setVerified(true);
	}
	protected String getBindDN()
	{
		String bindDN = .get();
		if(bindDN == null || bindDN.length() == 0)
		{
			PicketBoxLogger.LOGGER.traceBindDNNotFound();
		}
		return bindDN;
	}
	{
		String bindCredential = .get();
		if (bindCredential.startsWith("{EXT}"))
		{
			try
			{
				bindCredential = new String(org.jboss.security.Util.loadPassword(bindCredential));
			}
			catch (Exception e1)
			{
				PicketBoxLogger.LOGGER.errorDecryptingBindCredential(e1);
			}			
		}
		String securityDomain = .get();
		if (securityDomain != null)
		{
			try
			{
				ObjectName serviceName = new ObjectName(securityDomain);
				char[] tmp = DecodeAction.decode(bindCredentialserviceName);
				bindCredential = new String(tmp);
			catch (Exception e)
			{
				PicketBoxLogger.LOGGER.errorDecryptingBindCredential(e);
			}
		}
		return bindCredential;
	}
	protected void setPasswordCallbackValue(Object thePassPasswordCallback passwdCallback)
		String tmp;
		if(thePass instanceof String)
		{
		    tmp = (StringthePass;
			passwdCallback.setPassword(tmp.toCharArray());
		}
		else if(thePass instanceof char[])
		{
			passwdCallback.setPassword((char[])thePass); 
		else if(thePass instanceof byte[])
		{
			byte[] theBytes = (byte[]) thePass;
			passwdCallback.setPassword((new String(theBytes).toCharArray())); 
		else
		{
			throw PicketBoxMessages.MESSAGES.invalidPasswordType(thePass != null ? thePass.getClass() : null);
		}
	}
	{
		Properties env = new Properties();
		for (Entry<StringStringentry : .entrySet())
		{
			env.put(entry.getKey(), entry.getValue());
		}
		// Set defaults for key values if they are missing
		if (factoryName == null)
		{
			factoryName = "com.sun.jndi.ldap.LdapCtxFactory";
		}
		if (authType == null)
		if (providerURL == null)
			providerURL = "ldap://localhost:" + ((protocol != null && protocol.equals("ssl")) ? "636" : "389");
		env.setProperty(.providerURL);
	      if ( == null)
	           = "distinguishedName";
	      
		// JBAS-3555, allow anonymous login with no bindDN and bindCredential
		if (dn != null)
		if (credential != null)
			env.put(.credential);
        PicketBoxLogger.LOGGER.traceLDAPConnectionEnv(env);
        return new InitialLdapContext(envnull);
	}

Parameters:
ctx - the context to search from
user - the input username
credential - the bind credential
baseDN - base DN to search the ctx from
filter - the search filter string
Returns:
the userDN string for the successful authentication
Throws:
NamingException
 
   @SuppressWarnings("rawtypes")
   protected String bindDNAuthentication(InitialLdapContext ctxString userObject credentialString baseDN,
         String filterthrows NamingException
   {
      SearchControls constraints = new SearchControls();
      constraints.setTimeLimit();
      String attrList[] = {};
      constraints.setReturningAttributes(attrList);
      NamingEnumeration results = null;
      Object[] filterArgs = {user};
      results = ctx.search(baseDNfilterfilterArgsconstraints);
      if (results.hasMore() == false)
      {
         results.close();
         throw PicketBoxMessages.MESSAGES.failedToFindBaseContextDN(baseDN);
      }
      SearchResult sr = (SearchResultresults.next();
      String name = sr.getName();
      String userDN = null;
      Attributes attrs = sr.getAttributes();
      if (attrs != null)
      {
          Attribute dn = attrs.get();
          if (dn != null)
          {
                  userDN = (Stringdn.get();
          }
      }
      if (userDN == null)
      {
          if (sr.isRelative() == true)
              userDN = name + ("".equals(baseDN) ? "" : "," + baseDN);
          else
              throw PicketBoxMessages.MESSAGES.unableToFollowReferralForAuth(name);
      }
      safeClose(results);
      results = null;
      
      InitialLdapContext userCtx = constructInitialLdapContext(userDNcredential);
      safeClose(userCtx);
      
      return userDN;
   }
	@SuppressWarnings("rawtypes")
	protected void safeClose(NamingEnumeration results)
	{
		if(results != null)
		{
			try 
			{
				results.close();
catch (NamingException e) {}
		}
	}
	protected void safeClose(InitialLdapContext ic)
	{
		if(ic != null)
		{
			try 
			{
				ic.close();
			catch (NamingException e
			}
		}
	}
New to GrepCode? Check out our FAQ X