|
|
|
@ -36,6 +36,7 @@ import javax.naming.directory.SearchControls; |
|
|
|
import javax.naming.ldap.ExtendedRequest; |
|
|
|
import javax.naming.ldap.ExtendedRequest; |
|
|
|
import javax.naming.ldap.ExtendedResponse; |
|
|
|
import javax.naming.ldap.ExtendedResponse; |
|
|
|
import javax.naming.ldap.LdapContext; |
|
|
|
import javax.naming.ldap.LdapContext; |
|
|
|
|
|
|
|
import javax.naming.ldap.LdapName; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
@ -49,6 +50,7 @@ import org.springframework.ldap.core.DirContextAdapter; |
|
|
|
import org.springframework.ldap.core.DistinguishedName; |
|
|
|
import org.springframework.ldap.core.DistinguishedName; |
|
|
|
import org.springframework.ldap.core.LdapTemplate; |
|
|
|
import org.springframework.ldap.core.LdapTemplate; |
|
|
|
import org.springframework.ldap.core.SearchExecutor; |
|
|
|
import org.springframework.ldap.core.SearchExecutor; |
|
|
|
|
|
|
|
import org.springframework.ldap.support.LdapNameBuilder; |
|
|
|
import org.springframework.security.authentication.BadCredentialsException; |
|
|
|
import org.springframework.security.authentication.BadCredentialsException; |
|
|
|
import org.springframework.security.core.Authentication; |
|
|
|
import org.springframework.security.core.Authentication; |
|
|
|
import org.springframework.security.core.GrantedAuthority; |
|
|
|
import org.springframework.security.core.GrantedAuthority; |
|
|
|
@ -93,7 +95,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid"); |
|
|
|
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid"); |
|
|
|
|
|
|
|
|
|
|
|
/** The DN under which groups are stored */ |
|
|
|
/** The DN under which groups are stored */ |
|
|
|
private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups"); |
|
|
|
private LdapName groupSearchBase = LdapNameBuilder.newInstance("cn=groups").build(); |
|
|
|
|
|
|
|
|
|
|
|
/** Password attribute name */ |
|
|
|
/** Password attribute name */ |
|
|
|
private String passwordAttributeName = "userPassword"; |
|
|
|
private String passwordAttributeName = "userPassword"; |
|
|
|
@ -137,14 +139,14 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public UserDetails loadUserByUsername(String username) { |
|
|
|
public UserDetails loadUserByUsername(String username) { |
|
|
|
DistinguishedName dn = this.usernameMapper.buildDn(username); |
|
|
|
LdapName dn = this.usernameMapper.buildLdapName(username); |
|
|
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, username); |
|
|
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, username); |
|
|
|
this.logger.debug(LogMessage.format("Loading user '%s' with DN '%s'", username, dn)); |
|
|
|
this.logger.debug(LogMessage.format("Loading user '%s' with DN '%s'", username, dn)); |
|
|
|
DirContextAdapter userCtx = loadUserAsContext(dn, username); |
|
|
|
DirContextAdapter userCtx = loadUserAsContext(dn, username); |
|
|
|
return this.userDetailsMapper.mapUserFromContext(userCtx, username, authorities); |
|
|
|
return this.userDetailsMapper.mapUserFromContext(userCtx, username, authorities); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) { |
|
|
|
private DirContextAdapter loadUserAsContext(final LdapName dn, final String username) { |
|
|
|
return (DirContextAdapter) this.template.executeReadOnly((ContextExecutor) (ctx) -> { |
|
|
|
return (DirContextAdapter) this.template.executeReadOnly((ContextExecutor) (ctx) -> { |
|
|
|
try { |
|
|
|
try { |
|
|
|
Attributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve); |
|
|
|
Attributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve); |
|
|
|
@ -188,7 +190,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
"No authentication object found in security context. Can't change current user's password!"); |
|
|
|
"No authentication object found in security context. Can't change current user's password!"); |
|
|
|
String username = authentication.getName(); |
|
|
|
String username = authentication.getName(); |
|
|
|
this.logger.debug(LogMessage.format("Changing password for user '%s'", username)); |
|
|
|
this.logger.debug(LogMessage.format("Changing password for user '%s'", username)); |
|
|
|
DistinguishedName userDn = this.usernameMapper.buildDn(username); |
|
|
|
LdapName userDn = this.usernameMapper.buildLdapName(username); |
|
|
|
if (this.usePasswordModifyExtensionOperation) { |
|
|
|
if (this.usePasswordModifyExtensionOperation) { |
|
|
|
changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword); |
|
|
|
changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -204,13 +206,13 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
* @return the granted authorities returned by the group search |
|
|
|
* @return the granted authorities returned by the group search |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
List<GrantedAuthority> getUserAuthorities(final DistinguishedName dn, final String username) { |
|
|
|
List<GrantedAuthority> getUserAuthorities(final LdapName dn, final String username) { |
|
|
|
SearchExecutor se = (ctx) -> { |
|
|
|
SearchExecutor se = (ctx) -> { |
|
|
|
DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx); |
|
|
|
LdapName fullDn = LdapUtils.getFullDn(dn, ctx); |
|
|
|
SearchControls ctrls = new SearchControls(); |
|
|
|
SearchControls ctrls = new SearchControls(); |
|
|
|
ctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName }); |
|
|
|
ctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName }); |
|
|
|
return ctx.search(this.groupSearchBase, this.groupSearchFilter, new String[] { fullDn.toUrl(), username }, |
|
|
|
return ctx.search(this.groupSearchBase, this.groupSearchFilter, |
|
|
|
ctrls); |
|
|
|
new String[] { fullDn.toString(), username }, ctrls); |
|
|
|
}; |
|
|
|
}; |
|
|
|
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper); |
|
|
|
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper); |
|
|
|
this.template.search(se, roleCollector); |
|
|
|
this.template.search(se, roleCollector); |
|
|
|
@ -221,7 +223,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
public void createUser(UserDetails user) { |
|
|
|
public void createUser(UserDetails user) { |
|
|
|
DirContextAdapter ctx = new DirContextAdapter(); |
|
|
|
DirContextAdapter ctx = new DirContextAdapter(); |
|
|
|
copyToContext(user, ctx); |
|
|
|
copyToContext(user, ctx); |
|
|
|
DistinguishedName dn = this.usernameMapper.buildDn(user.getUsername()); |
|
|
|
LdapName dn = this.usernameMapper.buildLdapName(user.getUsername()); |
|
|
|
this.logger.debug(LogMessage.format("Creating new user '%s' with DN '%s'", user.getUsername(), dn)); |
|
|
|
this.logger.debug(LogMessage.format("Creating new user '%s' with DN '%s'", user.getUsername(), dn)); |
|
|
|
this.template.bind(dn, ctx, null); |
|
|
|
this.template.bind(dn, ctx, null); |
|
|
|
// Check for any existing authorities which might be set for this
|
|
|
|
// Check for any existing authorities which might be set for this
|
|
|
|
@ -235,7 +237,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void updateUser(UserDetails user) { |
|
|
|
public void updateUser(UserDetails user) { |
|
|
|
DistinguishedName dn = this.usernameMapper.buildDn(user.getUsername()); |
|
|
|
LdapName dn = this.usernameMapper.buildLdapName(user.getUsername()); |
|
|
|
this.logger.debug(LogMessage.format("Updating new user '%s' with DN '%s'", user.getUsername(), dn)); |
|
|
|
this.logger.debug(LogMessage.format("Updating new user '%s' with DN '%s'", user.getUsername(), dn)); |
|
|
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername()); |
|
|
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername()); |
|
|
|
DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername()); |
|
|
|
DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername()); |
|
|
|
@ -260,14 +262,14 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void deleteUser(String username) { |
|
|
|
public void deleteUser(String username) { |
|
|
|
DistinguishedName dn = this.usernameMapper.buildDn(username); |
|
|
|
LdapName dn = this.usernameMapper.buildLdapName(username); |
|
|
|
removeAuthorities(dn, getUserAuthorities(dn, username)); |
|
|
|
removeAuthorities(dn, getUserAuthorities(dn, username)); |
|
|
|
this.template.unbind(dn); |
|
|
|
this.template.unbind(dn); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean userExists(String username) { |
|
|
|
public boolean userExists(String username) { |
|
|
|
DistinguishedName dn = this.usernameMapper.buildDn(username); |
|
|
|
LdapName dn = this.usernameMapper.buildLdapName(username); |
|
|
|
try { |
|
|
|
try { |
|
|
|
Object obj = this.template.lookup(dn); |
|
|
|
Object obj = this.template.lookup(dn); |
|
|
|
if (obj instanceof Context) { |
|
|
|
if (obj instanceof Context) { |
|
|
|
@ -285,33 +287,48 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
* @param group the name of the group |
|
|
|
* @param group the name of the group |
|
|
|
* @return the DN of the corresponding group, including the groupSearchBase |
|
|
|
* @return the DN of the corresponding group, including the groupSearchBase |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
protected DistinguishedName buildGroupDn(String group) { |
|
|
|
protected DistinguishedName buildGroupDn(String group) { |
|
|
|
DistinguishedName dn = new DistinguishedName(this.groupSearchBase); |
|
|
|
DistinguishedName dn = new DistinguishedName(this.groupSearchBase); |
|
|
|
dn.add(this.groupRoleAttributeName, group.toLowerCase()); |
|
|
|
dn.add(this.groupRoleAttributeName, group.toLowerCase()); |
|
|
|
return dn; |
|
|
|
return dn; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected LdapName buildGroupName(String group) { |
|
|
|
|
|
|
|
return LdapNameBuilder.newInstance(buildGroupDn(group)).build(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected void copyToContext(UserDetails user, DirContextAdapter ctx) { |
|
|
|
protected void copyToContext(UserDetails user, DirContextAdapter ctx) { |
|
|
|
this.userDetailsMapper.mapUserToContext(user, ctx); |
|
|
|
this.userDetailsMapper.mapUserToContext(user, ctx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Deprecated |
|
|
|
protected void addAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
protected void addAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE); |
|
|
|
modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.ADD_ATTRIBUTE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void addAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
|
|
|
|
addAuthorities(new DistinguishedName(userDn), authorities); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Deprecated |
|
|
|
protected void removeAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
protected void removeAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE); |
|
|
|
modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.REMOVE_ATTRIBUTE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void removeAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) { |
|
|
|
|
|
|
|
removeAuthorities(new DistinguishedName(userDn), authorities); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void modifyAuthorities(final DistinguishedName userDn, |
|
|
|
private void modifyAuthorities(final LdapName userDn, final Collection<? extends GrantedAuthority> authorities, |
|
|
|
final Collection<? extends GrantedAuthority> authorities, final int modType) { |
|
|
|
final int modType) { |
|
|
|
this.template.executeReadWrite((ContextExecutor) (ctx) -> { |
|
|
|
this.template.executeReadWrite((ContextExecutor) (ctx) -> { |
|
|
|
for (GrantedAuthority authority : authorities) { |
|
|
|
for (GrantedAuthority authority : authorities) { |
|
|
|
String group = convertAuthorityToGroup(authority); |
|
|
|
String group = convertAuthorityToGroup(authority); |
|
|
|
DistinguishedName fullDn = LdapUtils.getFullDn(userDn, ctx); |
|
|
|
LdapName fullDn = LdapUtils.getFullDn(userDn, ctx); |
|
|
|
ModificationItem addGroup = new ModificationItem(modType, |
|
|
|
ModificationItem addGroup = new ModificationItem(modType, |
|
|
|
new BasicAttribute(this.groupMemberAttributeName, fullDn.toUrl())); |
|
|
|
new BasicAttribute(this.groupMemberAttributeName, fullDn.toString())); |
|
|
|
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup }); |
|
|
|
ctx.modifyAttributes(buildGroupName(group), new ModificationItem[] { addGroup }); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
}); |
|
|
|
}); |
|
|
|
@ -334,7 +351,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setGroupSearchBase(String groupSearchBase) { |
|
|
|
public void setGroupSearchBase(String groupSearchBase) { |
|
|
|
this.groupSearchBase = new DistinguishedName(groupSearchBase); |
|
|
|
this.groupSearchBase = LdapNameBuilder.newInstance(groupSearchBase).build(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setGroupRoleAttributeName(String groupRoleAttributeName) { |
|
|
|
public void setGroupRoleAttributeName(String groupRoleAttributeName) { |
|
|
|
@ -413,8 +430,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
this.rolePrefix = rolePrefix; |
|
|
|
this.rolePrefix = rolePrefix; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void changePasswordUsingAttributeModification(DistinguishedName userDn, String oldPassword, |
|
|
|
private void changePasswordUsingAttributeModification(LdapName userDn, String oldPassword, String newPassword) { |
|
|
|
String newPassword) { |
|
|
|
|
|
|
|
ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE, |
|
|
|
ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE, |
|
|
|
new BasicAttribute(this.passwordAttributeName, newPassword)) }; |
|
|
|
new BasicAttribute(this.passwordAttributeName, newPassword)) }; |
|
|
|
if (oldPassword == null) { |
|
|
|
if (oldPassword == null) { |
|
|
|
@ -438,11 +454,10 @@ public class LdapUserDetailsManager implements UserDetailsManager { |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void changePasswordUsingExtensionOperation(DistinguishedName userDn, String oldPassword, |
|
|
|
private void changePasswordUsingExtensionOperation(LdapName userDn, String oldPassword, String newPassword) { |
|
|
|
String newPassword) { |
|
|
|
|
|
|
|
this.template.executeReadWrite((dirCtx) -> { |
|
|
|
this.template.executeReadWrite((dirCtx) -> { |
|
|
|
LdapContext ctx = (LdapContext) dirCtx; |
|
|
|
LdapContext ctx = (LdapContext) dirCtx; |
|
|
|
String userIdentity = LdapUtils.getFullDn(userDn, ctx).encode(); |
|
|
|
String userIdentity = LdapUtils.getFullDn(userDn, ctx).toString(); |
|
|
|
PasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword); |
|
|
|
PasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword); |
|
|
|
try { |
|
|
|
try { |
|
|
|
return ctx.extendedOperation(request); |
|
|
|
return ctx.extendedOperation(request); |
|
|
|
|