From db042046e9935a18884298aa62c8194a7d3f8e13 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Mon, 15 May 2006 19:34:57 +0000 Subject: [PATCH] Introduce LDAPUserDetails. --- .../userdetails/ldap/LdapUserDetails.java | 30 +++ .../userdetails/ldap/LdapUserDetailsImpl.java | 183 ++++++++++++++++++ .../ldap/LdapUserDetailsMapper.java | 82 ++++++++ 3 files changed, 295 insertions(+) create mode 100644 core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetails.java create mode 100644 core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsImpl.java create mode 100644 core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java diff --git a/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetails.java b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetails.java new file mode 100644 index 0000000000..5b2fcb5ae6 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetails.java @@ -0,0 +1,30 @@ +package org.acegisecurity.userdetails.ldap; + +import org.acegisecurity.userdetails.UserDetails; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.Control; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public interface LdapUserDetails extends UserDetails { + /** + * @return the DN of the entry for this user's account. + */ + String getDn(); + + /** + * @return the attributes for the user's entry in the directory (or a subset of them, + * depending on what was retrieved). + */ + Attributes getAttributes(); + + /** + * Returns any LDAP response controls (as part of a user authentication process, for example). + * + * @return an array of LDAP Control instances, never null + */ + Control[] getControls(); +} diff --git a/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsImpl.java b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsImpl.java new file mode 100644 index 0000000000..cfc671175d --- /dev/null +++ b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsImpl.java @@ -0,0 +1,183 @@ +package org.acegisecurity.userdetails.ldap; + +import org.acegisecurity.GrantedAuthority; + +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.ldap.Control; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A UserDetails implementation which is used internally by the Ldap services. + * + * It also contains the user's distinguished name and a set of attributes that + * have been retrieved from the Ldap server. + *

+ * An instance may be created as the result of a search, or when user information + * is retrieved during authentication. + *

+ *

+ * An instance of this class will be used by the LdapAuthenticationProvider + * to construct the final user details object that it returns. + *

+ * + * @author Luke Taylor + * @version $Id$ + */ +public class LdapUserDetailsImpl implements LdapUserDetails { + + private static final GrantedAuthority[] NO_AUTHORITIES = new GrantedAuthority[0]; + private static final Control[] NO_CONTROLS = new Control[0]; + + //~ Instance fields ======================================================== + + private String dn; + private Attributes attributes = new BasicAttributes(); + private String username; + private String password; + private boolean enabled = true; + private boolean accountNonExpired = true; + private boolean credentialsNonExpired = true; + private boolean accountNonLocked = true; + private GrantedAuthority[] authorities = NO_AUTHORITIES; + private Control[] controls = NO_CONTROLS; + + //~ Constructors =========================================================== + + protected LdapUserDetailsImpl() { + } + + //~ Methods ================================================================ + + public String getDn() { + return dn; + } + + public Attributes getAttributes() { + return attributes; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean isEnabled() { + return enabled; + } + + public boolean isAccountNonExpired() { + return accountNonExpired; + } + + public boolean isCredentialsNonExpired() { + return credentialsNonExpired; + } + + public boolean isAccountNonLocked() { + return accountNonLocked; + } + + public GrantedAuthority[] getAuthorities() { + return authorities; + } + + public Control[] getControls() { + return controls; + } + + //~ Inner classes ========================================================== + + /** Variation of essence pattern. Used to create mutable intermediate object */ + public static class Essence { + + LdapUserDetailsImpl instance = new LdapUserDetailsImpl(); + + List mutableAuthorities = new ArrayList(); + + public Essence() { + } + + public Essence(LdapUserDetails copyMe) { + instance.dn = copyMe.getDn(); + instance.attributes = copyMe.getAttributes(); + instance.username = copyMe.getUsername(); + instance.password = copyMe.getPassword(); + instance.enabled = copyMe.isEnabled(); + instance.accountNonExpired = copyMe.isAccountNonExpired(); + instance.credentialsNonExpired = copyMe.isCredentialsNonExpired(); + instance.accountNonLocked = copyMe.isAccountNonLocked(); + instance.controls = copyMe.getControls(); + mutableAuthorities = Arrays.asList(copyMe.getAuthorities()); + } + + public Essence setDn(String dn) { + instance.dn = dn; + return this; + } + + public Essence setAttributes(Attributes attributes) { + instance.attributes = attributes; + return this; + } + + public Essence setUsername(String username) { + instance.username = username; + return this; + } + + public Essence setPassword(String password) { + instance.password = password; + return this; + } + + public Essence setEnabled(boolean enabled) { + instance.enabled = enabled; + return this; + } + + public Essence setAccountNonExpired(boolean accountNonExpired) { + instance.accountNonExpired = accountNonExpired; + return this; + } + + public Essence setCredentialsNonExpired(boolean credentialsNonExpired) { + instance.credentialsNonExpired = credentialsNonExpired; + return this; + } + + public Essence setAccountNonLocked(boolean accountNonLocked) { + instance.accountNonLocked = accountNonLocked; + return this; + } + + public Essence setAuthorities(GrantedAuthority[] authorities) { + mutableAuthorities = Arrays.asList(authorities); + + return this; + } + + public Essence addAuthority(GrantedAuthority a) { + mutableAuthorities.add(a); + + return this; + } + + public GrantedAuthority[] getGrantedAuthorities() { + return (GrantedAuthority[])mutableAuthorities.toArray(new GrantedAuthority[0]); + } + + public LdapUserDetails createUserDetails() { + //TODO: Validation of properties + + instance.authorities = getGrantedAuthorities(); + + return instance; + } + } +} diff --git a/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java new file mode 100644 index 0000000000..763f87a4d2 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java @@ -0,0 +1,82 @@ +package org.acegisecurity.userdetails.ldap; + +import org.acegisecurity.ldap.LdapEntryMapper; +import org.acegisecurity.GrantedAuthorityImpl; +import org.springframework.util.Assert; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.naming.directory.Attributes; +import javax.naming.directory.Attribute; +import javax.naming.NamingException; +import javax.naming.NamingEnumeration; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public class LdapUserDetailsMapper implements LdapEntryMapper { + private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class); + + private String passwordAttributeName = "userPassword"; + + private String[] roleAttributes = null; + + private String rolePrefix = "ROLE_"; + + private boolean convertToUpperCase = true; + + public void setPasswordAttributeName(String passwordAttributeName) { + this.passwordAttributeName = passwordAttributeName; + } + + public void setRoleAttributes(String[] roleAttributes) { + Assert.notNull(roleAttributes, "roleAttributes array cannot be null"); + this.roleAttributes = roleAttributes; + } + + public Object mapAttributes(String dn, Attributes attributes) throws NamingException { + LdapUserDetailsImpl.Essence essence = new LdapUserDetailsImpl.Essence(); + + essence.setDn(dn); + essence.setAttributes(attributes); + + Attribute passwordAttribute = attributes.get(passwordAttributeName); + + if(passwordAttribute != null) { + Object retrievedPassword = passwordAttribute.get(); + + if (!(retrievedPassword instanceof String)) { + // Assume it's binary + retrievedPassword = new String((byte[])retrievedPassword); + } + + essence.setPassword((String)retrievedPassword); + } + + // Map the roles + + for(int i=0; roleAttributes != null && i < roleAttributes.length; i++) { + Attribute roleAttribute = attributes.get(roleAttributes[i]); + + NamingEnumeration attributeRoles = roleAttribute.getAll(); + + while(attributeRoles.hasMore()) { + Object role = attributeRoles.next(); + + // We only handle Strings for the time being + if(role instanceof String) { + if(convertToUpperCase) { + role = ((String)role).toUpperCase(); + } + + essence.addAuthority(new GrantedAuthorityImpl(rolePrefix + role)); + } else { + logger.warn("Non-String value found for role attribute " + roleAttribute.getID()); + } + } + } + + return essence.createUserDetails(); + } +}