From 139d8c2f651c422496ce22754737061bc74d205a Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Fri, 25 Aug 2006 16:14:50 +0000 Subject: [PATCH] Experimental UserDetailsManager interface and some ldap implementation classes. --- .../userdetails/UserDetailsManager.java | 25 +++ .../userdetails/ldap/InetOrgPerson.java | 50 +++++ .../ldap/LdapUserDetailsManager.java | 192 ++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 sandbox/src/main/java/org/acegisecurity/userdetails/UserDetailsManager.java create mode 100644 sandbox/src/main/java/org/acegisecurity/userdetails/ldap/InetOrgPerson.java create mode 100644 sandbox/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsManager.java diff --git a/sandbox/src/main/java/org/acegisecurity/userdetails/UserDetailsManager.java b/sandbox/src/main/java/org/acegisecurity/userdetails/UserDetailsManager.java new file mode 100644 index 0000000000..9b42eb4966 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/userdetails/UserDetailsManager.java @@ -0,0 +1,25 @@ +package org.acegisecurity.userdetails; + +/** + * An extension of the {@link UserDetailsService} which provides the ability + * to create new users and update existing ones. + * + * @author Luke + * @version $Id$ + */ +public interface UserDetailsManager extends UserDetailsService { + + /** + * Save details for the supplied user, or update + * + * @param user + */ + void createUser(UserDetails user); + + void updateUser(UserDetails user); + + void deleteUser(String username); + + boolean userExists(String username); + +} diff --git a/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/InetOrgPerson.java b/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/InetOrgPerson.java new file mode 100644 index 0000000000..eae81f398f --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/InetOrgPerson.java @@ -0,0 +1,50 @@ +package org.acegisecurity.userdetails.ldap; + +/** + * @author Luke + * @version $Id$ + */ +public class InetOrgPerson extends LdapUserDetailsImpl { + String sn; + String cn; + + public String getSn() { + return sn; + } + + public String getCn() { + return cn; + } + + public static class Essence extends LdapUserDetailsImpl.Essence { + + public Essence() { + } + + public Essence(InetOrgPerson copyMe) { + super(copyMe); + } + + LdapUserDetailsImpl createTarget() { + return new InetOrgPerson(); + } + + public void setSn(String sn) { + ((InetOrgPerson)instance).sn = sn; + } + + public void setCn(String cn) { + ((InetOrgPerson)instance).cn = cn; + } + } + + public static void main(String[] args) { + InetOrgPerson.Essence p = new InetOrgPerson.Essence(); + + p.setSn("Scobbie"); + + InetOrgPerson immutablePerson = (InetOrgPerson)p.createUserDetails(); + System.out.println(immutablePerson.getSn()); + + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsManager.java b/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsManager.java new file mode 100644 index 0000000000..b34ce1392f --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsManager.java @@ -0,0 +1,192 @@ +package org.acegisecurity.userdetails.ldap; + +import org.acegisecurity.userdetails.UserDetailsManager; +import org.acegisecurity.userdetails.UserDetails; +import org.acegisecurity.userdetails.UsernameNotFoundException; +import org.acegisecurity.ldap.LdapUtils; +import org.acegisecurity.ldap.ContextSourceInitialDirContextFactory; +import org.acegisecurity.providers.encoding.PasswordEncoder; +import org.acegisecurity.providers.ldap.authenticator.LdapShaPasswordEncoder; +import org.springframework.dao.DataAccessException; +import org.springframework.util.Assert; +import org.springframework.beans.BeanWrapperImpl; +import net.sf.ldaptemplate.ContextSource; +import net.sf.ldaptemplate.LdapTemplate; +import net.sf.ldaptemplate.EntryNotFoundException; +import net.sf.ldaptemplate.ContextMapper; +import net.sf.ldaptemplate.support.DistinguishedName; +import net.sf.ldaptemplate.support.DirContextOperations; +import net.sf.ldaptemplate.support.DirContextAdapter; + +import javax.naming.Context; +import java.util.*; + +/** + * UserDetails manager. Based on the "Person" sample dao from spring-ldap. + * + * @author Luke + * @version $Id$ + */ +public class LdapUserDetailsManager implements UserDetailsManager { + private String usernameAttributeName; + private DistinguishedName userDnBase; + private LdapTemplate template; + + private String groupBase="cn=groups"; + private String groupRoleName="cn"; + private String rolePrefix = "ROLE_"; + + private ContextMapper mapper; + + private String[] objectClasses = new String[] {"top", "person", "organizationalPerson", "inetOrgPerson"}; + + /** Map of user details properties to ldap attributes */ + private Map attributeMapping; + + public static void main(String[] args) { + ContextSourceInitialDirContextFactory contextFactory = new ContextSourceInitialDirContextFactory("ldap://192.168.101.100:389/dc=acegisecurity,dc=com,dc=au"); + contextFactory.setManagerDn("uid=acegiman,cn=people,dc=acegisecurity,dc=com,dc=au"); + contextFactory.setManagerPassword("password"); + + LdapUserDetailsManager mgr = new LdapUserDetailsManager(contextFactory); + + InetOrgPerson.Essence user = new InetOrgPerson.Essence(); + user.setUsername("jerrymouse"); + user.setSn("User"); + user.setCn("Test User"); + PasswordEncoder pwe = new LdapShaPasswordEncoder(); + user.setPassword(pwe.encodePassword("wheresthecheese", null)); + + mgr.updateUser(user.createUserDetails()); + } + + public LdapUserDetailsManager(ContextSource contextSource) { + template = new LdapTemplate(contextSource); + userDnBase = new DistinguishedName("cn=users"); + Map defaultMapping = new HashMap(); + + defaultMapping.put("username", "cn"); + defaultMapping.put("password", "userPassword"); + + attributeMapping = Collections.unmodifiableMap(defaultMapping); + } + + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { + DistinguishedName dn = buildDn(username); + + return (UserDetails) template.lookup(dn, mapper); + } + + public void createUser(UserDetails user) { + template.bind(buildDn(user.getUsername()), getUserContextToBind(user), null); + } + + public void updateUser(UserDetails user) throws UsernameNotFoundException { + template.rebind(buildDn(user.getUsername()), getUserContextToBind(user), null); + } + + public void deleteUser(String username) throws UsernameNotFoundException { + DistinguishedName dn = buildDn(username); + template.unbind(dn); + } + + DirContextOperations getUserContextToBind(UserDetails user) { + DirContextAdapter adapter = new DirContextAdapter(); + + Map attributesToSet = new HashMap(); + attributesToSet.put("objectclass", objectClasses); + + BeanWrapperImpl userBean = new BeanWrapperImpl(user); + Iterator properties = attributeMapping.keySet().iterator(); + + while(properties.hasNext()) { + String property = (String) properties.next(); + String attribute = (String) attributeMapping.get(property); + + List values = (List) attributesToSet.get(attribute); + if(values == null) { + values = new ArrayList(); + attributesToSet.put(attribute, values); + } + + Object propertyValue = userBean.getPropertyValue(property); + Assert.notNull(propertyValue); + + values.add(propertyValue); + } + + Iterator attributes = attributesToSet.keySet().iterator(); + + while(attributes.hasNext()) { + String attributeName = (String) attributes.next(); + List values = (List) attributesToSet.get(attributeName); + adapter.setAttributeValues(attributeName, values.toArray()); + } + + return adapter; + } + + public boolean userExists(String username) { + DistinguishedName dn = buildDn(username); + + try { + Object obj = template.lookup(dn); + if (obj instanceof Context) { + LdapUtils.closeContext((Context) obj); + } + return true; + } catch(EntryNotFoundException e) { + return false; + } + } + + DistinguishedName buildDn(String username) { + DistinguishedName dn = new DistinguishedName(userDnBase); + + dn.add(usernameAttributeName, username); + + return dn; + } + + public void setGroupBase(String groupBase) { + this.groupBase = groupBase; + } + + public void setGroupRoleName(String groupRoleName) { + this.groupRoleName = groupRoleName; + } + + public void setUserDnBase(String userDnBase) { + this.userDnBase = new DistinguishedName(userDnBase); + } + + /** + * Sets the mapping from property names on the UserDetails object to + * directory attributes. + * + * @param attributeMapping the map, keyed by property name. + */ + public void setAttributeMapping(Map attributeMapping) { + Assert.notNull(attributeMapping.get("username"), "Mapping must contain an entry for 'username'"); + Assert.notNull(attributeMapping.get("password"), "Mapping must contain an entry for 'password'"); + usernameAttributeName = (String) attributeMapping.get("username"); + this.attributeMapping = Collections.unmodifiableMap(attributeMapping); + } +} + +class UserDetailsContextMapper implements ContextMapper { + private Class type; + private Map attributeMapping; + + public UserDetailsContextMapper(Map attributeMapping, Class userDetailsType) { + type = userDetailsType; + this.attributeMapping = attributeMapping; + } + + public Object mapFromContext(Object ctx) { + DirContextOperations dirContext = (DirContextOperations) ctx; + DistinguishedName dn = new DistinguishedName(dirContext.getDn()); + + return null; + } +}