SEC-513: First check in of user management stuff.
This commit is contained in:
parent
9b71b5aa00
commit
70239a9769
|
@ -0,0 +1,42 @@
|
|||
package org.acegisecurity.userdetails;
|
||||
|
||||
/**
|
||||
* An extension of the {@link UserDetailsService} which provides the ability
|
||||
* to create new users and update existing ones.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface UserDetailsManager extends UserDetailsService {
|
||||
|
||||
/**
|
||||
* Create a new user with the supplied details.
|
||||
*/
|
||||
void createUser(UserDetails user);
|
||||
|
||||
/**
|
||||
* Update the specified user.
|
||||
*/
|
||||
void updateUser(UserDetails user);
|
||||
|
||||
/**
|
||||
* Remove the user with the given login name from the system.
|
||||
*/
|
||||
void deleteUser(String username);
|
||||
|
||||
/**
|
||||
* Modify the current user's password.
|
||||
*
|
||||
*
|
||||
* @param oldPassword current password (for re-authentication if required)
|
||||
* @param newPassword the password to change to
|
||||
*/
|
||||
void changePassword(String oldPassword, String newPassword);
|
||||
|
||||
/**
|
||||
* Check if a user with the supplied login name exists in the system.
|
||||
*/
|
||||
boolean userExists(String username);
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
|
||||
/**
|
||||
* UserDetails implementation whose properties are based on a subset of the
|
||||
* LDAP schema for <tt>inetOrgPerson</tt>.
|
||||
*
|
||||
* <p>
|
||||
* The username will be mapped from the <tt>uid</tt> attribute by default.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InetOrgPerson extends Person {
|
||||
private String mail;
|
||||
private String uid;
|
||||
private String employeeNumber;
|
||||
private String destinationIndicator;
|
||||
|
||||
public String getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getEmployeeNumber() {
|
||||
return employeeNumber;
|
||||
}
|
||||
|
||||
public String getDestinationIndicator() {
|
||||
return destinationIndicator;
|
||||
}
|
||||
|
||||
protected void populateContext(DirContextAdapter adapter) {
|
||||
super.populateContext(adapter);
|
||||
adapter.setAttributeValue("mail", mail);
|
||||
adapter.setAttributeValue("uid", uid);
|
||||
adapter.setAttributeValue("employeeNumber", employeeNumber);
|
||||
adapter.setAttributeValue("destinationIndicator", destinationIndicator);
|
||||
adapter.setAttributeValues("objectclass", new String[] {"top", "person", "organizationalPerson", "inetOrgPerson"});
|
||||
}
|
||||
|
||||
public static class Essence extends Person.Essence {
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
public Essence(InetOrgPerson copyMe) {
|
||||
super(copyMe);
|
||||
setMail(copyMe.getMail());
|
||||
setUid(copyMe.getUid());
|
||||
setDestinationIndicator(copyMe.getDestinationIndicator());
|
||||
setEmployeeNumber(copyMe.getEmployeeNumber());
|
||||
}
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
super(ctx);
|
||||
setMail(ctx.getStringAttribute("mail"));
|
||||
setUid(ctx.getStringAttribute("uid"));
|
||||
setEmployeeNumber(ctx.getStringAttribute("employeeNumber"));
|
||||
setDestinationIndicator(ctx.getStringAttribute("destinationIndicator"));
|
||||
}
|
||||
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new InetOrgPerson();
|
||||
}
|
||||
|
||||
public void setMail(String email) {
|
||||
((InetOrgPerson) instance).mail = email;
|
||||
}
|
||||
|
||||
public void setUid(String uid) {
|
||||
((InetOrgPerson) instance).uid = uid;
|
||||
|
||||
if(instance.getUsername() == null) {
|
||||
setUsername(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEmployeeNumber(String no) {
|
||||
((InetOrgPerson) instance).employeeNumber = no;
|
||||
}
|
||||
|
||||
public void setDestinationIndicator(String destination) {
|
||||
((InetOrgPerson) instance).destinationIndicator = destination;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InetOrgPersonContextMapper implements UserDetailsContextMapper {
|
||||
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, GrantedAuthority[] authorities) {
|
||||
InetOrgPerson.Essence p = new InetOrgPerson.Essence(ctx);
|
||||
|
||||
p.setUsername(username);
|
||||
p.setAuthorities(authorities);
|
||||
|
||||
return p.createUserDetails();
|
||||
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
Assert.isInstanceOf(InetOrgPerson.class, user, "UserDetails must be an InetOrgPerson instance");
|
||||
|
||||
InetOrgPerson p = (InetOrgPerson) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ public interface LdapUserDetails extends UserDetails {
|
|||
* The attributes for the user's entry in the directory (or a subset of them, depending on what was
|
||||
* retrieved from the directory)
|
||||
*
|
||||
* @deprecated Map additional attributes to properties in a subclass rather than accessing them here.
|
||||
* @return the user's attributes, or an empty array if none were obtained, never null.
|
||||
*/
|
||||
Attributes getAttributes();
|
||||
|
|
|
@ -18,10 +18,13 @@ package org.acegisecurity.userdetails.ldap;
|
|||
import org.acegisecurity.GrantedAuthority;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
|
@ -30,10 +33,14 @@ import javax.naming.ldap.Control;
|
|||
|
||||
/**
|
||||
* 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.<p>An instance may be
|
||||
* created as the result of a search, or when user information is retrieved during authentication.</p>
|
||||
* <p>An instance of this class will be used by the <tt>LdapAuthenticationProvider</tt> to construct the final
|
||||
* user details object that it returns.</p>
|
||||
* distinguished name and a set of attributes that have been retrieved from the Ldap server.
|
||||
* <p>
|
||||
* An instance may be created as the result of a search, or when user information is retrieved during authentication.
|
||||
* </p>
|
||||
* <p>
|
||||
* An instance of this class will be used by the <tt>LdapAuthenticationProvider</tt> to construct the final user details
|
||||
* object that it returns.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
|
@ -41,7 +48,6 @@ import javax.naming.ldap.Control;
|
|||
public class LdapUserDetailsImpl implements LdapUserDetails {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final GrantedAuthority[] NO_AUTHORITIES = new GrantedAuthority[0];
|
||||
private static final Control[] NO_CONTROLS = new Control[0];
|
||||
|
||||
|
@ -110,10 +116,14 @@ public class LdapUserDetailsImpl implements LdapUserDetails {
|
|||
* Variation of essence pattern. Used to create mutable intermediate object
|
||||
*/
|
||||
public static class Essence {
|
||||
private LdapUserDetailsImpl instance = createTarget();
|
||||
protected LdapUserDetailsImpl instance = createTarget();
|
||||
private List mutableAuthorities = new ArrayList();
|
||||
|
||||
public Essence() {}
|
||||
public Essence() { }
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
setDn(ctx.getDn().toString());
|
||||
}
|
||||
|
||||
public Essence(LdapUserDetails copyMe) {
|
||||
setDn(copyMe.getDn());
|
||||
|
@ -128,14 +138,27 @@ public class LdapUserDetailsImpl implements LdapUserDetails {
|
|||
setAuthorities(copyMe.getAuthorities());
|
||||
}
|
||||
|
||||
LdapUserDetailsImpl createTarget() {
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new LdapUserDetailsImpl();
|
||||
}
|
||||
|
||||
public Essence addAuthority(GrantedAuthority a) {
|
||||
mutableAuthorities.add(a);
|
||||
/** Adds the authority to the list, unless it is already there, in which case it is ignored */
|
||||
public void addAuthority(GrantedAuthority a) {
|
||||
if(!hasAuthority(a)) {
|
||||
mutableAuthorities.add(a);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
private boolean hasAuthority(GrantedAuthority a) {
|
||||
Iterator authorities = mutableAuthorities.iterator();
|
||||
|
||||
while(authorities.hasNext()) {
|
||||
GrantedAuthority authority = (GrantedAuthority) authorities.next();
|
||||
if(authority.equals(a)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public LdapUserDetails createUserDetails() {
|
||||
|
@ -155,62 +178,44 @@ public class LdapUserDetailsImpl implements LdapUserDetails {
|
|||
return (GrantedAuthority[]) mutableAuthorities.toArray(new GrantedAuthority[0]);
|
||||
}
|
||||
|
||||
public Essence setAccountNonExpired(boolean accountNonExpired) {
|
||||
public void setAccountNonExpired(boolean accountNonExpired) {
|
||||
instance.accountNonExpired = accountNonExpired;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setAccountNonLocked(boolean accountNonLocked) {
|
||||
public void setAccountNonLocked(boolean accountNonLocked) {
|
||||
instance.accountNonLocked = accountNonLocked;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setAttributes(Attributes attributes) {
|
||||
public void setAttributes(Attributes attributes) {
|
||||
instance.attributes = attributes;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setAuthorities(GrantedAuthority[] authorities) {
|
||||
public void setAuthorities(GrantedAuthority[] authorities) {
|
||||
mutableAuthorities = new ArrayList(Arrays.asList(authorities));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setControls(Control[] controls) {
|
||||
instance.controls = controls;
|
||||
}
|
||||
|
||||
public Essence setCredentialsNonExpired(boolean credentialsNonExpired) {
|
||||
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
|
||||
instance.credentialsNonExpired = credentialsNonExpired;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setDn(String dn) {
|
||||
public void setDn(String dn) {
|
||||
instance.dn = dn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setEnabled(boolean enabled) {
|
||||
public void setEnabled(boolean enabled) {
|
||||
instance.enabled = enabled;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setPassword(String password) {
|
||||
public void setPassword(String password) {
|
||||
instance.password = password;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Essence setUsername(String username) {
|
||||
public void setUsername(String username) {
|
||||
instance.username = username;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.acegisecurity.userdetails.UserDetailsManager;
|
||||
import org.acegisecurity.ldap.LdapUtils;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ldap.support.DistinguishedName;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.ldap.LdapTemplate;
|
||||
import org.springframework.ldap.AttributesMapper;
|
||||
import org.springframework.ldap.ContextSource;
|
||||
import org.springframework.ldap.ContextExecutor;
|
||||
import org.springframework.ldap.SearchExecutor;
|
||||
import org.springframework.ldap.EntryNotFoundException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
import javax.naming.directory.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An Ldap implementation of UserDetailsManager.
|
||||
* <p>
|
||||
* It is designed around a standard setup where users and groups/roles are stored under separate contexts,
|
||||
* defined by the "userDnBase" and "groupSearchBase" properties respectively.
|
||||
* </p>
|
||||
* <p>
|
||||
* In this case, LDAP is being used purely to retrieve information and this class can be used in place of any other
|
||||
* UserDetailsService for authentication. Authentication isn't performed directly against the directory, unlike with the
|
||||
* LDAP authentication provider setup.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsManager.class);
|
||||
|
||||
/** The DN under which users entries are stored */
|
||||
private DistinguishedName userDnBase = new DistinguishedName("cn=users");
|
||||
/** The DN under which groups are stored */
|
||||
private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups");
|
||||
|
||||
/** The attribute which contains the user login name, and which is used by default to build the DN for new users */
|
||||
private String usernameAttributeName = "uid";
|
||||
/** Password attribute name */
|
||||
private String passwordAttributeName = "userPassword";
|
||||
|
||||
/** The attribute which corresponds to the role name of a group. */
|
||||
private String groupRoleAttributeName ="cn";
|
||||
/** The attribute which contains members of a group */
|
||||
private String groupMemberAttributeName = "uniquemember";
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
/** The pattern to be used for the user search. {0} is the user's DN */
|
||||
private String groupSearchFilter = "(uniquemember={0})";
|
||||
/**
|
||||
* The strategy used to create a UserDetails object from the LDAP context, username and list of authorities.
|
||||
* This should be set to match the required UserDetails implementation.
|
||||
*/
|
||||
private UserDetailsContextMapper userDetailsMapper = new InetOrgPersonContextMapper();
|
||||
|
||||
private LdapTemplate template;
|
||||
|
||||
/** Default context mapper used to create a set of roles from a list of attributes */
|
||||
private AttributesMapper roleMapper = new AttributesMapper() {
|
||||
|
||||
public Object mapFromAttributes(Attributes attributes) throws NamingException {
|
||||
Attribute roleAttr = attributes.get(groupRoleAttributeName);
|
||||
|
||||
NamingEnumeration ne = roleAttr.getAll();
|
||||
// assert ne.hasMore();
|
||||
Object group = ne.next();
|
||||
String role = group.toString();
|
||||
|
||||
return new GrantedAuthorityImpl(rolePrefix + role.toUpperCase());
|
||||
}
|
||||
};
|
||||
|
||||
private String[] attributesToRetrieve = null;
|
||||
|
||||
public LdapUserDetailsManager(ContextSource contextSource) {
|
||||
template = new LdapTemplate(contextSource);
|
||||
}
|
||||
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||
DistinguishedName dn = buildDn(username);
|
||||
GrantedAuthority[] authorities = getUserAuthorities(dn, username);
|
||||
|
||||
logger.debug("Loading user '"+ username + "' with DN '" + dn + "'");
|
||||
|
||||
DirContextAdapter userCtx = loadUserAsContext(dn, username);
|
||||
|
||||
return userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
|
||||
}
|
||||
|
||||
private UserContext loadUserAsContext(final DistinguishedName dn, final String username) {
|
||||
return (UserContext) template.executeReadOnly(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
try {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
return new UserContext(attrs, LdapUtils.getFullDn(dn, ctx));
|
||||
} catch(NameNotFoundException notFound) {
|
||||
throw new UsernameNotFoundException("User " + username + " not found", notFound);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the password for the current user. The username is obtained from the security context.
|
||||
* <p>
|
||||
* If the old password is supplied, the update will be made by rebinding as the user, thus modifying the password
|
||||
* using the user's permissions. If <code>oldPassword</code> is null, the update will be attempted using a
|
||||
* standard read/write context supplied by the context source.
|
||||
* </p>
|
||||
*
|
||||
* @param oldPassword the old password
|
||||
* @param newPassword the new value of the password.
|
||||
*/
|
||||
public void changePassword(final String oldPassword, final String newPassword) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
Assert.notNull(authentication,
|
||||
"No authentication object found in security context. Can't change current user's password!");
|
||||
|
||||
String username = authentication.getName();
|
||||
|
||||
|
||||
|
||||
|
||||
logger.debug("Changing password for user '"+ username);
|
||||
|
||||
final DistinguishedName dn = buildDn(username);
|
||||
final ModificationItem[] passwordChange = new ModificationItem[] {
|
||||
new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword))
|
||||
};
|
||||
|
||||
if(oldPassword == null) {
|
||||
template.modifyAttributes(dn, passwordChange);
|
||||
return;
|
||||
}
|
||||
|
||||
template.executeReadWrite(new ContextExecutor() {
|
||||
|
||||
public Object executeWithContext(DirContext dirCtx) throws NamingException {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
ctx.removeFromEnvironment("com.sun.jndi.ldap.connect.pool");
|
||||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, LdapUtils.getFullDn(dn, ctx).toUrl());
|
||||
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, oldPassword);
|
||||
ctx.reconnect(null);
|
||||
|
||||
ctx.modifyAttributes(dn, passwordChange);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dn the distinguished name of the entry - may be either relative to the base context
|
||||
* or a complete DN including the name of the context (either is supported).
|
||||
* @param username the user whose roles are required.
|
||||
* @return the granted authorities returned by the group search
|
||||
*/
|
||||
GrantedAuthority[] getUserAuthorities(final DistinguishedName dn, final String username) {
|
||||
SearchExecutor se = new SearchExecutor() {
|
||||
public NamingEnumeration executeSearch(DirContext ctx) throws NamingException {
|
||||
DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx);
|
||||
SearchControls ctrls = new SearchControls();
|
||||
ctrls.setReturningAttributes(new String[] {groupRoleAttributeName});
|
||||
|
||||
return ctx.search(groupSearchBase, groupSearchFilter, new String[] {fullDn.toUrl(), username}, ctrls);
|
||||
}
|
||||
};
|
||||
|
||||
LdapTemplate.AttributesMapperCallbackHandler roleCollector =
|
||||
template.new AttributesMapperCallbackHandler(roleMapper);
|
||||
|
||||
template.search(se, roleCollector);
|
||||
List authorities = roleCollector.getList();
|
||||
|
||||
return (GrantedAuthority[]) authorities.toArray(new GrantedAuthority[authorities.size()]);
|
||||
}
|
||||
|
||||
// protected String getRoleFilter(DistinguishedName dn, String username) {
|
||||
// return new EqualsFilter("uniquemember", dn.toString()).encode();
|
||||
// }
|
||||
|
||||
public void createUser(UserDetails user) {
|
||||
DirContextAdapter ctx = new DirContextAdapter();
|
||||
copyToContext(user, ctx);
|
||||
DistinguishedName dn = buildDn(user.getUsername());
|
||||
// Check for any existing authorities which might be set for this DN
|
||||
GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
|
||||
|
||||
if(authorities.length > 0) {
|
||||
removeAuthorities(dn, authorities);
|
||||
}
|
||||
|
||||
logger.debug("Creating new user '"+ user.getUsername() + "' with DN '" + dn + "'");
|
||||
|
||||
template.bind(dn, ctx, null);
|
||||
|
||||
addAuthorities(dn, user.getAuthorities());
|
||||
}
|
||||
|
||||
public void updateUser(UserDetails user) {
|
||||
// Assert.notNull(attributesToRetrieve, "Configuration must specify a list of attributes in order to use update.");
|
||||
DistinguishedName dn = buildDn(user.getUsername());
|
||||
|
||||
logger.debug("Updating user '"+ user.getUsername() + "' with DN '" + dn + "'");
|
||||
|
||||
GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
|
||||
|
||||
UserContext ctx = loadUserAsContext(dn, user.getUsername());
|
||||
ctx.setUpdateMode(true);
|
||||
copyToContext(user, ctx);
|
||||
|
||||
// Remove the objectclass attribute from the list of mods (if present).
|
||||
List mods = new LinkedList(Arrays.asList(ctx.getModificationItems()));
|
||||
|
||||
ListIterator modIt = mods.listIterator();
|
||||
while(modIt.hasNext()) {
|
||||
ModificationItem mod = (ModificationItem) modIt.next();
|
||||
Attribute a = mod.getAttribute();
|
||||
if("objectclass".equalsIgnoreCase(a.getID())) {
|
||||
modIt.remove();
|
||||
}
|
||||
}
|
||||
|
||||
template.modifyAttributes(dn, (ModificationItem[]) mods.toArray(new ModificationItem[mods.size()]));
|
||||
|
||||
// template.rebind(dn, ctx, null);
|
||||
// Remove the old authorities and replace them with the new one
|
||||
removeAuthorities(dn, authorities);
|
||||
addAuthorities(dn, user.getAuthorities());
|
||||
}
|
||||
|
||||
public void deleteUser(String username) {
|
||||
DistinguishedName dn = buildDn(username);
|
||||
removeAuthorities(dn, getUserAuthorities(dn, username));
|
||||
template.unbind(dn);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a DN from a username.
|
||||
* <p>
|
||||
* The default implementation appends a name component to the <tt>userDnBase</tt> context using the
|
||||
* <tt>usernameAttributeName</tt> property. So if the <tt>uid</tt> attribute is used to store the username, and the
|
||||
* base DN is <tt>cn=users</tt> and we are creating a new user called "sam", then the DN will be
|
||||
* <tt>uid=sam,cn=users</tt>.
|
||||
*
|
||||
* @param username the user name used for authentication.
|
||||
* @return the corresponding DN, relative to the base context.
|
||||
*/
|
||||
protected DistinguishedName buildDn(String username) {
|
||||
DistinguishedName dn = new DistinguishedName(userDnBase);
|
||||
|
||||
dn.add(usernameAttributeName, username);
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DN from a group name.
|
||||
*
|
||||
* @param group the name of the group
|
||||
* @return the DN of the corresponding group, including the groupSearchBase
|
||||
*/
|
||||
protected DistinguishedName buildGroupDn(String group) {
|
||||
DistinguishedName dn = new DistinguishedName(groupSearchBase);
|
||||
dn.add(groupRoleAttributeName, group.toLowerCase());
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
protected void copyToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
userDetailsMapper.mapUserToContext(user, ctx);
|
||||
}
|
||||
|
||||
private void addAuthorities(DistinguishedName userDn, GrantedAuthority[] authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE);
|
||||
}
|
||||
|
||||
private void removeAuthorities(DistinguishedName userDn, GrantedAuthority[] authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
private void modifyAuthorities(final DistinguishedName userDn, final GrantedAuthority[] authorities, final int modType) {
|
||||
template.executeReadWrite(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
for(int i=0; i < authorities.length; i++) {
|
||||
GrantedAuthority authority = authorities[i];
|
||||
String group = convertAuthorityToGroup(authority);
|
||||
DistinguishedName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
||||
ModificationItem addGroup = new ModificationItem(modType,
|
||||
new BasicAttribute(groupMemberAttributeName, fullDn.toUrl()));
|
||||
|
||||
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] {addGroup});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String convertAuthorityToGroup(GrantedAuthority authority) {
|
||||
String group = authority.getAuthority();
|
||||
|
||||
if(group.startsWith(rolePrefix)) {
|
||||
group = group.substring(rolePrefix.length());
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setUsernameAttributeName(String usernameAttributeName) {
|
||||
this.usernameAttributeName = usernameAttributeName;
|
||||
}
|
||||
|
||||
public void setPasswordAttributeName(String passwordAttributeName) {
|
||||
this.passwordAttributeName = passwordAttributeName;
|
||||
}
|
||||
|
||||
public void setGroupSearchBase(String groupSearchBase) {
|
||||
this.groupSearchBase = new DistinguishedName(groupSearchBase);
|
||||
}
|
||||
|
||||
public void setGroupRoleAttributeName(String groupRoleAttributeName) {
|
||||
this.groupRoleAttributeName = groupRoleAttributeName;
|
||||
}
|
||||
|
||||
public void setUserDnBase(String userDnBase) {
|
||||
this.userDnBase = new DistinguishedName(userDnBase);
|
||||
}
|
||||
|
||||
public void setAttributesToRetrieve(String[] attributesToRetrieve) {
|
||||
Assert.notNull(attributesToRetrieve);
|
||||
this.attributesToRetrieve = attributesToRetrieve;
|
||||
}
|
||||
|
||||
public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {
|
||||
this.userDetailsMapper = userDetailsMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the multi-valued attribute which holds the DNs of users who are members of a group.
|
||||
* <p>
|
||||
* Usually this will be <tt>uniquemember</tt> (the default value) or <tt>member</tt>.
|
||||
* </p>
|
||||
*
|
||||
* @param groupMemberAttributeName the name of the attribute used to store group members.
|
||||
*/
|
||||
public void setGroupMemberAttributeName(String groupMemberAttributeName) {
|
||||
Assert.hasText(groupMemberAttributeName);
|
||||
this.groupMemberAttributeName = groupMemberAttributeName;
|
||||
this.groupSearchFilter = "(" + groupMemberAttributeName + "={0})";
|
||||
}
|
||||
|
||||
public void setRoleMapper(AttributesMapper roleMapper) {
|
||||
this.roleMapper = roleMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class allows us to set the <tt>updateMode</tt> property of DirContextAdapter when updating existing users.
|
||||
*/
|
||||
private static class UserContext extends DirContextAdapter {
|
||||
public UserContext(Attributes pAttrs, Name dn) {
|
||||
super(pAttrs, dn);
|
||||
}
|
||||
|
||||
protected void setUpdateMode(boolean mode) {
|
||||
super.setUpdateMode(mode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,10 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ldap.ContextMapper;
|
||||
import org.springframework.ldap.UncategorizedLdapException;
|
||||
import org.springframework.ldap.AttributesIntegrityViolationException;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
|
@ -37,10 +41,11 @@ import javax.naming.directory.Attributes;
|
|||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsMapper implements LdapEntryMapper {
|
||||
public class LdapUserDetailsMapper implements ContextMapper {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class);
|
||||
// private String usernameAttributeName = "uid";
|
||||
private String passwordAttributeName = "userPassword";
|
||||
private String rolePrefix = "ROLE_";
|
||||
private String[] roleAttributes = null;
|
||||
|
@ -48,42 +53,45 @@ public class LdapUserDetailsMapper implements LdapEntryMapper {
|
|||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public Object mapAttributes(String dn, Attributes attributes)
|
||||
throws NamingException {
|
||||
public Object mapFromContext(Object ctxObj) {
|
||||
Assert.isInstanceOf(DirContextAdapter.class, ctxObj, "Can only map from DirContextAdapter instances");
|
||||
|
||||
DirContextAdapter ctx = (DirContextAdapter)ctxObj;
|
||||
String dn = ctx.getNameInNamespace();
|
||||
|
||||
logger.debug("Mapping user details from context with DN: " + dn);
|
||||
|
||||
LdapUserDetailsImpl.Essence essence = new LdapUserDetailsImpl.Essence();
|
||||
|
||||
essence.setDn(dn);
|
||||
essence.setAttributes(attributes);
|
||||
essence.setAttributes(ctx.getAttributes());
|
||||
|
||||
Attribute passwordAttribute = attributes.get(passwordAttributeName);
|
||||
Attribute passwordAttribute = ctx.getAttributes().get(passwordAttributeName);
|
||||
|
||||
if (passwordAttribute != null) {
|
||||
essence.setPassword(mapPassword(passwordAttribute));
|
||||
}
|
||||
|
||||
// essence.setUsername(mapUsername(ctx));
|
||||
|
||||
// Map the roles
|
||||
for (int i = 0; (roleAttributes != null) && (i < roleAttributes.length); i++) {
|
||||
Attribute roleAttribute = attributes.get(roleAttributes[i]);
|
||||
String[] rolesForAttribute = ctx.getStringAttributes(roleAttributes[i]);
|
||||
|
||||
if (roleAttribute == null) {
|
||||
if (rolesForAttribute == null) {
|
||||
logger.debug("Couldn't read role attribute '" + roleAttributes[i] + "' for user " + dn);
|
||||
continue;
|
||||
}
|
||||
|
||||
NamingEnumeration attributeRoles = roleAttribute.getAll();
|
||||
|
||||
while (attributeRoles.hasMore()) {
|
||||
GrantedAuthority authority = createAuthority(attributeRoles.next());
|
||||
for (int j = 0; j < rolesForAttribute.length; j++) {
|
||||
GrantedAuthority authority = createAuthority(rolesForAttribute[j]);
|
||||
|
||||
if (authority != null) {
|
||||
essence.addAuthority(authority);
|
||||
} else {
|
||||
logger.debug("Failed to create an authority value from attribute with Id: "
|
||||
+ roleAttribute.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return essence.createUserDetails();
|
||||
return essence;
|
||||
}
|
||||
|
||||
|
@ -94,8 +102,14 @@ public class LdapUserDetailsMapper implements LdapEntryMapper {
|
|||
* @param passwordAttribute the attribute instance containing the password
|
||||
* @return a String representation of the password.
|
||||
*/
|
||||
protected String mapPassword(Attribute passwordAttribute) throws NamingException {
|
||||
Object retrievedPassword = passwordAttribute.get();
|
||||
protected String mapPassword(Attribute passwordAttribute) {
|
||||
Object retrievedPassword = null;
|
||||
|
||||
try {
|
||||
retrievedPassword = passwordAttribute.get();
|
||||
} catch (NamingException e) {
|
||||
throw new UncategorizedLdapException("Failed to get password attribute", e);
|
||||
}
|
||||
|
||||
if (!(retrievedPassword instanceof String)) {
|
||||
// Assume it's binary
|
||||
|
@ -106,6 +120,24 @@ public class LdapUserDetailsMapper implements LdapEntryMapper {
|
|||
|
||||
}
|
||||
|
||||
// protected String mapUsername(DirContextAdapter ctx) {
|
||||
// Attribute usernameAttribute = ctx.getAttributes().get(usernameAttributeName);
|
||||
// String username;
|
||||
//
|
||||
// if (usernameAttribute == null) {
|
||||
// throw new AttributesIntegrityViolationException(
|
||||
// "Failed to get attribute " + usernameAttributeName + " from context");
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// username = (String) usernameAttribute.get();
|
||||
// } catch (NamingException e) {
|
||||
// throw new UncategorizedLdapException("Failed to get username from attribute " + usernameAttributeName, e);
|
||||
// }
|
||||
//
|
||||
// return username;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates a GrantedAuthority from a role attribute. Override to customize
|
||||
* authority object creation.
|
||||
|
@ -148,10 +180,15 @@ public class LdapUserDetailsMapper implements LdapEntryMapper {
|
|||
this.passwordAttributeName = passwordAttributeName;
|
||||
}
|
||||
|
||||
|
||||
// public void setUsernameAttributeName(String usernameAttributeName) {
|
||||
// this.usernameAttributeName = usernameAttributeName;
|
||||
// }
|
||||
|
||||
/**
|
||||
* The names of any attributes in the user's entry which represent application
|
||||
* roles. These will be converted to <tt>GrantedAuthority</tt>s and added to the
|
||||
* list in the returned LdapUserDetails object.
|
||||
* list in the returned LdapUserDetails object. The attribute values must be Strings by default.
|
||||
*
|
||||
* @param roleAttributes the names of the role attributes.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.acegisecurity.ldap.LdapUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* UserDetails implementation whose properties are based on the LDAP schema for <tt>Person</tt>.
|
||||
*
|
||||
* @author Luke
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class Person extends LdapUserDetailsImpl {
|
||||
private String sn;
|
||||
private List cn = new ArrayList();
|
||||
|
||||
protected Person() {
|
||||
}
|
||||
|
||||
public String getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public String[] getCn() {
|
||||
return (String[]) cn.toArray(new String[cn.size()]);
|
||||
}
|
||||
|
||||
protected void populateContext(DirContextAdapter adapter) {
|
||||
adapter.setAttributeValue("sn", sn);
|
||||
adapter.setAttributeValues("cn", getCn());
|
||||
|
||||
if(getPassword() != null) {
|
||||
adapter.setAttributeValue("userPassword", getPassword());
|
||||
}
|
||||
adapter.setAttributeValues("objectclass", new String[] {"top", "person"});
|
||||
}
|
||||
|
||||
public static class Essence extends LdapUserDetailsImpl.Essence {
|
||||
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
super(ctx);
|
||||
setCn(ctx.getStringAttributes("cn"));
|
||||
setSn(ctx.getStringAttribute("sn"));
|
||||
Object passo = ctx.getObjectAttribute("userPassword");
|
||||
|
||||
if(passo != null) {
|
||||
String password = LdapUtils.convertPasswordToString(passo);
|
||||
setPassword(password);
|
||||
}
|
||||
}
|
||||
|
||||
public Essence(Person copyMe) {
|
||||
super(copyMe);
|
||||
setSn(copyMe.sn);
|
||||
((Person) instance).cn = new ArrayList(copyMe.cn);
|
||||
}
|
||||
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new Person();
|
||||
}
|
||||
|
||||
public void setSn(String sn) {
|
||||
((Person) instance).sn = sn;
|
||||
}
|
||||
|
||||
public void setCn(String[] cn) {
|
||||
((Person) instance).cn = Arrays.asList(cn);
|
||||
}
|
||||
|
||||
public void addCn(String value) {
|
||||
((Person) instance).cn.add(value);
|
||||
}
|
||||
|
||||
public LdapUserDetails createUserDetails() {
|
||||
Person p = (Person) super.createUserDetails();
|
||||
Assert.hasLength(p.sn);
|
||||
Assert.notNull(p.cn);
|
||||
Assert.notEmpty(p.cn);
|
||||
// TODO: Check contents for null entries
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PersonContextMapper implements UserDetailsContextMapper {
|
||||
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, GrantedAuthority[] authorities) {
|
||||
Person.Essence p = new Person.Essence(ctx);
|
||||
|
||||
p.setUsername(username);
|
||||
p.setAuthorities(authorities);
|
||||
|
||||
return p.createUserDetails();
|
||||
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
Assert.isInstanceOf(Person.class, user, "UserDetails must be a Person instance");
|
||||
|
||||
Person p = (Person) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.springframework.ldap.support.DirContextOperations;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
|
||||
/**
|
||||
* Operations to map a UserDetails object to and from a Spring LDAP <tt>DirContextOperations</tt> implementation.
|
||||
* Used by LdapUserDetailsManager when loading and saving/creating user information.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface UserDetailsContextMapper {
|
||||
|
||||
/**
|
||||
* Creates a fully populated UserDetails object for use by the security framework.
|
||||
*
|
||||
* @param ctx the context object which contains the user information.
|
||||
* @param username the user's supplied login name.
|
||||
* @param authority the list of authorities which the user should be given.
|
||||
* @return the user object.
|
||||
*/
|
||||
UserDetails mapUserFromContext(DirContextOperations ctx, String username, GrantedAuthority[] authority);
|
||||
|
||||
/**
|
||||
* Reverse of the above operation. Populates a context object from the supplied user object.
|
||||
* Called when saving a user, for example.
|
||||
*/
|
||||
void mapUserToContext(UserDetails user, DirContextAdapter ctx);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.ldap.support.DistinguishedName;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InetOrgPersonTests extends TestCase {
|
||||
|
||||
public void testUsernameIsMappedFromContextUidIfNotSet() {
|
||||
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
|
||||
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
|
||||
|
||||
assertEquals("ghengis", p.getUsername());
|
||||
|
||||
}
|
||||
|
||||
public void testUsernameIsDifferentFromContextUidIfSet() {
|
||||
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
|
||||
essence.setUsername("joe");
|
||||
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
|
||||
|
||||
assertEquals("joe", p.getUsername());
|
||||
assertEquals("ghengis", p.getUid());
|
||||
}
|
||||
|
||||
public void testAttributesMapCorrectlyFromContext() {
|
||||
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
|
||||
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
|
||||
|
||||
assertEquals("ghengis@mongolia", p.getMail());
|
||||
assertEquals("Khan", p.getSn());
|
||||
assertEquals("Ghengis Khan", p.getCn()[0]);
|
||||
assertEquals("00001", p.getEmployeeNumber());
|
||||
assertEquals("West", p.getDestinationIndicator());
|
||||
}
|
||||
|
||||
public void testPasswordIsSetFromContextUserPassword() {
|
||||
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
|
||||
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
|
||||
|
||||
assertEquals("pillage", p.getPassword());
|
||||
|
||||
}
|
||||
|
||||
private DirContextAdapter createUserContext() {
|
||||
DirContextAdapter ctx = new DirContextAdapter();
|
||||
|
||||
ctx.setDn(new DistinguishedName("ignored=ignored"));
|
||||
ctx.setAttributeValue("uid", "ghengis");
|
||||
ctx.setAttributeValue("userPassword", "pillage");
|
||||
ctx.setAttributeValue("mail", "ghengis@mongolia");
|
||||
ctx.setAttributeValue("cn", "Ghengis Khan");
|
||||
ctx.setAttributeValue("sn", "Khan");
|
||||
ctx.setAttributeValue("employeeNumber", "00001");
|
||||
ctx.setAttributeValue("destinationIndicator", "West");
|
||||
ctx.setAttributeValue("o", "Hordes");
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.acegisecurity.userdetails.ldap;
|
||||
|
||||
import org.acegisecurity.ldap.AbstractLdapServerTestCase;
|
||||
import org.acegisecurity.ldap.LdapUtils;
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.springframework.ldap.LdapTemplate;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.ldap.support.DistinguishedName;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsManagerTests extends AbstractLdapServerTestCase {
|
||||
private static final GrantedAuthority[] TEST_AUTHORITIES = new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_CLOWNS"),
|
||||
new GrantedAuthorityImpl("ROLE_ACROBATS")};
|
||||
private LdapUserDetailsManager mgr;
|
||||
private LdapTemplate template;
|
||||
|
||||
protected void onSetUp() {
|
||||
mgr = new LdapUserDetailsManager(getInitialCtxFactory());
|
||||
template = new LdapTemplate(getInitialCtxFactory());
|
||||
DirContextAdapter ctx = new DirContextAdapter();
|
||||
|
||||
ctx.setAttributeValue("objectclass", "organizationalUnit");
|
||||
ctx.setAttributeValue("ou", "testpeople");
|
||||
template.bind("ou=testpeople", ctx, null);
|
||||
|
||||
ctx.setAttributeValue("ou", "testgroups");
|
||||
template.bind("ou=testgroups", ctx, null);
|
||||
|
||||
DirContextAdapter group = new DirContextAdapter();
|
||||
|
||||
group.setAttributeValue("objectclass", "groupOfNames");
|
||||
group.setAttributeValue("cn", "clowns");
|
||||
template.bind("cn=clowns,ou=testgroups", ctx, null);
|
||||
|
||||
group.setAttributeValue("cn", "acrobats");
|
||||
template.bind("cn=acrobats,ou=testgroups", ctx, null);
|
||||
|
||||
mgr.setUserDnBase("ou=testpeople");
|
||||
mgr.setGroupSearchBase("ou=testgroups");
|
||||
mgr.setGroupRoleAttributeName("cn");
|
||||
mgr.setGroupMemberAttributeName("member");
|
||||
mgr.setUserDetailsMapper(new PersonContextMapper());
|
||||
}
|
||||
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
Iterator people = template.list("ou=testpeople").iterator();
|
||||
|
||||
DirContext rootCtx = new DirContextAdapter(new DistinguishedName(getInitialCtxFactory().getRootDn()));
|
||||
|
||||
while(people.hasNext()) {
|
||||
template.unbind(LdapUtils.getRelativeName((String) people.next(), rootCtx));
|
||||
}
|
||||
|
||||
template.unbind("ou=testpeople");
|
||||
template.unbind("cn=acrobats,ou=testgroups");
|
||||
template.unbind("cn=clowns,ou=testgroups");
|
||||
template.unbind("ou=testgroups");
|
||||
|
||||
}
|
||||
|
||||
public void testLoadUserByUsernameReturnsCorrectData() {
|
||||
mgr.setUserDnBase("ou=people");
|
||||
mgr.setGroupSearchBase("ou=groups");
|
||||
UserDetails bob = mgr.loadUserByUsername("bob");
|
||||
assertEquals("bob", bob.getUsername());
|
||||
// password isn't read
|
||||
//assertEquals("bobspassword", bob.getPassword());
|
||||
|
||||
assertEquals(1, bob.getAuthorities().length);
|
||||
}
|
||||
|
||||
public void testLoadingInvalidUsernameThrowsUsernameNotFoundException() {
|
||||
|
||||
try {
|
||||
mgr.loadUserByUsername("jim");
|
||||
fail("Expected UsernameNotFoundException for user 'jim'");
|
||||
} catch(UsernameNotFoundException expected) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testUserExistsReturnsTrueForValidUser() {
|
||||
mgr.setUserDnBase("ou=people");
|
||||
assertTrue(mgr.userExists("bob"));
|
||||
}
|
||||
|
||||
public void testUserExistsReturnsFalseForInValidUser() {
|
||||
assertFalse(mgr.userExists("jim"));
|
||||
}
|
||||
|
||||
public void testCreateNewUserSucceeds() {
|
||||
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
|
||||
p.setCn(new String[] {"Joe Smeth"});
|
||||
p.setSn("Smeth");
|
||||
p.setUid("joe");
|
||||
p.setAuthorities(TEST_AUTHORITIES);
|
||||
|
||||
mgr.createUser(p.createUserDetails());
|
||||
}
|
||||
|
||||
public void testDeleteUserSucceeds() {
|
||||
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
|
||||
p.setCn(new String[] {"Don Smeth"});
|
||||
p.setSn("Smeth");
|
||||
p.setUid("don");
|
||||
p.setAuthorities(TEST_AUTHORITIES);
|
||||
|
||||
mgr.createUser(p.createUserDetails());
|
||||
mgr.setUserDetailsMapper(new InetOrgPersonContextMapper());
|
||||
|
||||
InetOrgPerson don = (InetOrgPerson) mgr.loadUserByUsername("don");
|
||||
|
||||
assertEquals(2, don.getAuthorities().length);
|
||||
|
||||
mgr.deleteUser("don");
|
||||
|
||||
try {
|
||||
mgr.loadUserByUsername("don");
|
||||
fail("Expected UsernameNotFoundException after deleting user");
|
||||
} catch(UsernameNotFoundException expected) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// Check that no authorities are left
|
||||
assertEquals(0, mgr.getUserAuthorities(mgr.buildDn("don"), "don").length);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ import javax.naming.directory.BasicAttributes;
|
|||
import javax.naming.directory.BasicAttribute;
|
||||
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.springframework.ldap.support.DirContextAdapter;
|
||||
import org.springframework.ldap.support.DistinguishedName;
|
||||
|
||||
/**
|
||||
* Tests {@link LdapUserDetailsMapper}.
|
||||
|
@ -38,14 +40,11 @@ public class LdapUserDetailsMapperTests extends TestCase {
|
|||
|
||||
mapper.setRoleAttributes(new String[] {"userRole"});
|
||||
|
||||
BasicAttributes attrs = new BasicAttributes();
|
||||
BasicAttribute roleAttribute = new BasicAttribute("userRole");
|
||||
roleAttribute.add("X");
|
||||
roleAttribute.add("Y");
|
||||
roleAttribute.add("Z");
|
||||
attrs.put(roleAttribute);
|
||||
DirContextAdapter ctx = new DirContextAdapter();
|
||||
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapAttributes("cn=someName", attrs);
|
||||
ctx.setAttributeValues("userRole", new String[] {"X", "Y", "Z"});
|
||||
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapFromContext(ctx);
|
||||
|
||||
assertEquals(3, user.getGrantedAuthorities().length);
|
||||
}
|
||||
|
@ -61,24 +60,28 @@ public class LdapUserDetailsMapperTests extends TestCase {
|
|||
BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("userRole", "x"));
|
||||
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapAttributes("cn=someName", attrs);
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
|
||||
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapFromContext(ctx);
|
||||
|
||||
assertEquals(1, user.getGrantedAuthorities().length);
|
||||
assertEquals("ROLE_X", user.getGrantedAuthorities()[0].getAuthority());
|
||||
}
|
||||
|
||||
public void testNonStringRoleAttributeIsIgnoredByDefault() throws Exception {
|
||||
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
|
||||
|
||||
mapper.setRoleAttributes(new String[] {"userRole"});
|
||||
|
||||
BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("userRole", new GrantedAuthorityImpl("X")));
|
||||
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapAttributes("cn=someName", attrs);
|
||||
|
||||
assertEquals(0, user.getGrantedAuthorities().length);
|
||||
}
|
||||
// public void testNonStringRoleAttributeIsIgnoredByDefault() throws Exception {
|
||||
// LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
|
||||
//
|
||||
// mapper.setRoleAttributes(new String[] {"userRole"});
|
||||
//
|
||||
// BasicAttributes attrs = new BasicAttributes();
|
||||
// attrs.put(new BasicAttribute("userRole", new GrantedAuthorityImpl("X")));
|
||||
//
|
||||
// DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
|
||||
//
|
||||
// LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) mapper.mapFromContext(ctx);
|
||||
//
|
||||
// assertEquals(0, user.getGrantedAuthorities().length);
|
||||
// }
|
||||
|
||||
public void testPasswordAttributeIsMappedCorrectly() throws Exception {
|
||||
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
|
||||
|
@ -87,8 +90,10 @@ public class LdapUserDetailsMapperTests extends TestCase {
|
|||
BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("myappsPassword", "mypassword".getBytes()));
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
|
||||
|
||||
LdapUserDetails user =
|
||||
((LdapUserDetailsImpl.Essence) mapper.mapAttributes("cn=someName", attrs)).createUserDetails();
|
||||
((LdapUserDetailsImpl.Essence) mapper.mapFromContext(ctx)).createUserDetails();
|
||||
|
||||
assertEquals("mypassword", user.getPassword());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue