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 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("", "(&(objectClass=user)(userPrincipalName="+email+"))",new UserAttributeMapper() );
if( lusers.size() > 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;
}
}
}
Some key sticking points I wan to point out:
- Check your existing users in AD to make sure you provide all required attributes
- 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 – user dissabled. This will allow user creation with out password verification.
- Password has to bent to AD in unicode format.
Here is portion of the spring config that pertains to the bean:
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base}" />
<property name="userDn" value="${ldap.admin.bind.userdn}" />
<property name="password" value="${ldap.admin.bind.password}" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="ldapContextSource" />
</bean>
<bean id="ldapUserDAO" class="com.trx.wfm.model.dao.LdapUserDAO">
<property name="ldapTemplate" ref="ldapTemplate" />
<property name="ldapReadonly" value="${ldap.readonly}" />
<property name="ldapBase" value="${ldap.base}" />
</bean>
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.
And on more point – 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’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).


















Hello. I just moved to the Miami area. I am looking for a solid company to help me with a refinance. I purchased the house from a short sale and had to do business over the phone because I was living in Az. I dont think that I got a good deal. My interest rate in now 6.99 percent. I think that I can refinance and get a much better deal. Please point me in the right direction.