<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Wet Feet - Online Marketing and Technology Blog &#187; ldap</title>
	<atom:link href="http://www.wetfeetblog.com/tag/ldap/feed" rel="self" type="application/rss+xml" />
	<link>http://www.wetfeetblog.com</link>
	<description></description>
	<lastBuildDate>Wed, 19 May 2010 15:34:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Using Microsoft Ad to Create and Authenticate Users</title>
		<link>http://www.wetfeetblog.com/spring-ldap-microsoft-ad/60</link>
		<comments>http://www.wetfeetblog.com/spring-ldap-microsoft-ad/60#comments</comments>
		<pubDate>Fri, 30 Jan 2009 15:38:38 +0000</pubDate>
		<dc:creator>Tomas Mazukna</dc:creator>
				<category><![CDATA[Spring Framework]]></category>
		<category><![CDATA[ldap]]></category>
		<category><![CDATA[microsoft ad]]></category>

		<guid isPermaLink="false">http://www.wetfeetblog.com/?p=60</guid>
		<description><![CDATA[I spent pas couple days digin in microsoft AD and spring LDAP. The job at hand was not pleasant, but after lots of pocking and test I managed to get the dag on thing to work. I can add/edit/delete/change password for a User stored in AD.  Here is the source code for the class: package [...]]]></description>
			<content:encoded><![CDATA[<p><strong>I spent pas couple days digin in microsoft AD and spring LDAP. The job at hand was not pleasant, but after lots of pocking and test I managed to get the dag on thing to work. I can add/edit/delete/change password for a User stored in AD.  Here is the source code for the class:</strong></p>
<p><span id="more-60"></span></p>
<pre>package com.trx.wfm.model.dao;

import java.io.UnsupportedEncodingException;
import java.util.List;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;

import org.apache.log4j.Logger;
import org.springframework.ldap.InvalidAttributeValueException;
import org.springframework.ldap.NameAlreadyBoundException;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.OperationNotSupportedException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;

import com.trx.wfm.exception.DuplicateUserException;
import com.trx.wfm.exception.LdapSaveException;
import com.trx.wfm.exception.PasswordStrengthException;
import com.trx.wfm.model.User;

public class LdapUserDAO {

    private LdapTemplate ldapTemplate;
    private boolean ldapReadonly;
    private String ldapBase;

	protected static final Logger logger = Logger.getLogger(LdapUserDAO.class);

    public LdapUserDAO() {
    }

    @SuppressWarnings("unchecked")
	public List getAllUsers() {
        return ldapTemplate.search("", "(objectclass=user)",new UserAttributeMapper() );
    }

    @SuppressWarnings("unchecked")
	public User findUser( String email ) {
        List lusers = ldapTemplate.search("", "(&amp;(objectClass=user)(userPrincipalName="+email+"))",new UserAttributeMapper() );
        if( lusers.size() &gt; 0 )
        	return lusers.get(0);
        return null;
    }

    public String getLdapBase() {
		return ldapBase;
	}

	public void setLdapBase(String ldapBase) {
		this.ldapBase = ldapBase;
	}

	public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

	public boolean isLdapReadonly() {
		return ldapReadonly;
	}

	public void setLdapReadonly(boolean ldapReadonly) {
		this.ldapReadonly = ldapReadonly;
	}

	public void deleteUser(User luzer) throws LdapSaveException {
		try {
		    DistinguishedName userDN = userToDistinguishedName( luzer );
		    ldapTemplate.unbind(userDN);
		} catch ( Exception exc ) {
			logger.error( "deleteUser()", exc);
			throw new LdapSaveException( exc.getMessage() );
		}
	}

	public void createUser(User luzer) throws DuplicateUserException, PasswordStrengthException, LdapSaveException {
		try {
		    Attributes personAttributes = new BasicAttributes();

		    personAttributes.put( "objectclass", "person" );
		    personAttributes.put( "objectclass", "user" );
		    personAttributes.put( "givenName", luzer.getFirstName() );
		    personAttributes.put( "userPrincipalName", luzer.getEmailAddress() );
		    personAttributes.put( "sn", luzer.getLastName());
		    personAttributes.put( "description", "Created via WFM 5.0 Flex app" );
		    personAttributes.put( "sAMAccountName", luzer.getFirstName().toUpperCase()+ "." + luzer.getLastName().toUpperCase() );
		    personAttributes.put( "userAccountControl", "512" ); /// 512 = normal luser

		    // PASSWORD stuff.....
		    personAttributes.put("unicodepwd", encodePassword( luzer.getPassword() ) );

		    // Set up user distinguished name and clreate it.
		    DistinguishedName newUserDN = userToDistinguishedName( luzer );
		    ldapTemplate.bind(newUserDN, null, personAttributes);

		} catch ( InvalidAttributeValueException exc ) {
			logger.error( "createUser()", exc);
			throw new LdapSaveException( exc.getMessage() );
		} catch ( NameAlreadyBoundException exc ) {   /// USER EXISTS....
			logger.error( "createUser()", exc);
			throw new DuplicateUserException(  "User ["+ luzer.getEmailAddress() + "] allready exists in AD." );
		} catch ( NameNotFoundException exc ) {
			logger.error( "createUser()", exc);
			throw new LdapSaveException( exc.getMessage() );
		} catch ( OperationNotSupportedException exc ) {  // CAN NOT ADD USER
			logger.error( "createUser()", exc);
			throw new PasswordStrengthException( exc.getMessage() );
		} catch ( Exception exc ) {
			logger.error( "createUser()", exc);
			throw new LdapSaveException( exc.getMessage() );
		}
	}

	public void changePassword( User luzer ) throws PasswordStrengthException {
		try {
		    ModificationItem repitem = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodepwd", encodePassword( luzer.getPassword() )) );
		    DistinguishedName userDN = userToDistinguishedName( luzer );
		    ldapTemplate.modifyAttributes( userDN, new ModificationItem[] { repitem } );
		} catch ( Exception exc ) {
			logger.error( "changePassword()", exc);
			throw new PasswordStrengthException( exc.getMessage() );
		}
	}

	public void updateUser( User luzer ) throws LdapSaveException {
		try {
		    ModificationItem repitem1 = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("givenName", luzer.getFirstName()) );
		    ModificationItem repitem2 = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("sn", luzer.getLastName()) );
		    ModificationItem repitem3 = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userPrincipalName", luzer.getEmailAddress()) );
		    DistinguishedName userDN = userToDistinguishedName( luzer );
		    ldapTemplate.modifyAttributes( userDN, new ModificationItem[] { repitem1, repitem2, repitem3 } );
		} catch ( Exception exc ) {
			logger.error( "updateUser()", exc);
			throw new LdapSaveException( exc.getMessage() );
		}
	}

	private byte[] encodePassword(String password) throws UnsupportedEncodingException {
		String newQuotedPassword = "\"" + password + "\"";
		return newQuotedPassword.getBytes("UTF-16LE");
	}	

	private DistinguishedName userToDistinguishedName( User luzer ) {
		return new DistinguishedName( "cn=user_"+luzer.getUserId() );
	}

	public class UserAttributeMapper implements AttributesMapper{

	    public Object mapFromAttributes(Attributes attributes) throws NamingException {
	        User user = new User();

	        user.setFirstName( attributes.get("givenName").get().toString() );
	        user.setLastName( attributes.get("sn").get().toString() );
	        user.setEmailAddress( attributes.get("userPrincipalName").get().toString() );

//        	logger.debug( " ====== ATTRIBUTES ============ " );
//	        NamingEnumeration list = attributes.getAll();
//	        while( list.hasMore() ) {
//	        	Attribute attr = list.next();
//	        	String output = " Attribute ID [" + attr.getID() + "] values [" ;
//	        	NamingEnumeration vals = attr.getAll();
//	        	while( vals.hasMore() )
//	        		output = output + "{" + vals.next().toString()+"},";
//	        	output = output + "]";
//	        	logger.debug( output );
//	        }
//
//        	logger.debug( " ============================== " );

	        return user;
	    }

	}

}</pre>
<p><strong>Some key sticking points I wan to point out:</strong></p>
<p><strong>- Check your existing users in AD to make sure you provide all required attributes</strong></p>
<p><strong>- OperationNotSupportedException usually indicates that something required is missing or password requirements are not met. If you are getting this exception change userAccountControl value to 2 &#8211; user dissabled. This will allow user creation with out password verification.</strong></p>
<p><strong>- Password has to bent to AD in unicode format.</strong></p>
<p><strong>Here is portion of the spring config that pertains to the bean:</strong></p>
<pre>	&lt;bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
		&lt;property name="url" value="${ldap.url}" /&gt;
		&lt;property name="base" value="${ldap.base}" /&gt;
		&lt;property name="userDn" value="${ldap.admin.bind.userdn}" /&gt;
		&lt;property name="password" value="${ldap.admin.bind.password}" /&gt;
	&lt;/bean&gt;

	&lt;bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"&gt;
		&lt;property name="contextSource" ref="ldapContextSource" /&gt;
	&lt;/bean&gt;

	&lt;bean id="ldapUserDAO" class="com.trx.wfm.model.dao.LdapUserDAO"&gt;
		&lt;property name="ldapTemplate" ref="ldapTemplate" /&gt;
		&lt;property name="ldapReadonly" value="${ldap.readonly}" /&gt;
		&lt;property name="ldapBase" value="${ldap.base}" /&gt;
	&lt;/bean&gt;</pre>
<p><strong>ldap.base points to the OU where the users are created. This simplifies the operations, since I am checking for users only on this branch of the tree and adding them there too.</strong></p>
<p><strong>And on more point &#8211; to change anythign in AD you need to go via secure connection ldaps://your.ldap.server:636. Most likly you will get an certificate error, since my server&#8217;s is self signed, so use keytool provided in java home/bin directory to import security certificates into key store. Make sure you specify the path of they keystore file! I did it wihout the keystore file and my app was still getting exceptions. You can provide they keystore location to java as VM startup parameter (RTFM).</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.wetfeetblog.com/spring-ldap-microsoft-ad/60/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
