mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-07 19:22:14 +00:00
SEC-284: added allowEmptyPasswords property with default value "true"
This commit is contained in:
parent
7957d54d67
commit
02e7bbb982
@ -35,39 +35,59 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link org.acegisecurity.providers.AuthenticationProvider} implementation that provides integration with an
|
* An {@link org.acegisecurity.providers.AuthenticationProvider} implementation that provides integration with an
|
||||||
* LDAP server.<p>There are many ways in which an LDAP directory can be configured so this class delegates most of
|
* LDAP server.
|
||||||
* its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator} and {@link
|
*
|
||||||
* LdapAuthoritiesPopulator}.</p>
|
* <p>There are many ways in which an LDAP directory can be configured so this class delegates most of
|
||||||
* <h3>LdapAuthenticator</h3>This interface is responsible for performing the user authentication and retrieving
|
* its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator}
|
||||||
|
* and {@link LdapAuthoritiesPopulator}.</p>
|
||||||
|
*
|
||||||
|
* <h3>LdapAuthenticator</h3>
|
||||||
|
* This interface is responsible for performing the user authentication and retrieving
|
||||||
* the user's information from the directory. Example implementations are {@link
|
* the user's information from the directory. Example implementations are {@link
|
||||||
* org.acegisecurity.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates the user by
|
* org.acegisecurity.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates the user by
|
||||||
* "binding" as that user, and {@link org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator
|
* "binding" as that user, and {@link org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator
|
||||||
* PasswordComparisonAuthenticator} which performs a comparison of the supplied password with the value stored in the
|
* PasswordComparisonAuthenticator} which performs a comparison of the supplied password with the value stored in the
|
||||||
* directory, either by retrieving the password or performing an LDAP "compare" operation.<p>The task of retrieving
|
* directory, either by retrieving the password or performing an LDAP "compare" operation.
|
||||||
* the user attributes is delegated to the authenticator because the permissions on the attributes may depend on the
|
* <p>The task of retrieving the user attributes is delegated to the authenticator because the permissions on the
|
||||||
* type of authentication being used; for example, if binding as the user, it may be necessary to read them with the
|
* attributes may depend on the type of authentication being used; for example, if binding as the user, it may be
|
||||||
* user's own permissions (using the same context used for the bind operation).</p>
|
* necessary to read them with the user's own permissions (using the same context used for the bind operation).</p>
|
||||||
* <h3>LdapAuthoritiesPopulator</h3>Once the user has been authenticated, this interface is called to obtain the
|
*
|
||||||
* set of granted authorities for the user. The {@link
|
* <h3>LdapAuthoritiesPopulator</h3>
|
||||||
* org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator} can be
|
* Once the user has been authenticated, this interface is called to obtain the set of granted authorities for the
|
||||||
* configured to obtain user role information from the user's attributes and/or to perform a search for "groups" that
|
* user.
|
||||||
* the user is a member of and map these to roles.<p>A custom implementation could obtain the roles from a
|
* The
|
||||||
* completely different source, for example from a database.</p>
|
* {@link org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator}
|
||||||
* <h3>Configuration</h3>A simple configuration might be as follows:<pre>
|
* can be configured to obtain user role information from the user's attributes and/or to perform a search for
|
||||||
|
* "groups" that the user is a member of and map these to roles.
|
||||||
|
*
|
||||||
|
* <p>A custom implementation could obtain the roles from a completely different source, for example from a database.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h3>Configuration</h3>A simple configuration might be as follows:
|
||||||
|
* <pre>
|
||||||
* <bean id="initialDirContextFactory" class="org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory">
|
* <bean id="initialDirContextFactory" class="org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory">
|
||||||
* <constructor-arg value="ldap://monkeymachine:389/dc=acegisecurity,dc=org"/>
|
* <constructor-arg value="ldap://monkeymachine:389/dc=acegisecurity,dc=org"/>
|
||||||
* <property name="managerDn"><value>cn=manager,dc=acegisecurity,dc=org</value></property>
|
* <property name="managerDn"><value>cn=manager,dc=acegisecurity,dc=org</value></property>
|
||||||
* <property name="managerPassword"><value>password</value></property> </bean>
|
* <property name="managerPassword"><value>password</value></property>
|
||||||
|
* </bean>
|
||||||
|
*
|
||||||
* <bean id="ldapAuthProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
|
* <bean id="ldapAuthProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
|
||||||
* <constructor-arg> <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
|
* <constructor-arg>
|
||||||
* <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
|
* <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
|
||||||
* <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property>
|
* <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
|
||||||
* </bean> </constructor-arg> <constructor-arg>
|
* <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property>
|
||||||
* <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
|
* </bean>
|
||||||
* <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
|
* </constructor-arg>
|
||||||
* <constructor-arg><value>ou=groups</value></constructor-arg>
|
* <constructor-arg>
|
||||||
* <property name="groupRoleAttribute"><value>ou</value></property> </bean>
|
* <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
|
||||||
* </constructor-arg> </bean></pre><p>This would set up the provider to access an LDAP server with URL
|
* <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
|
||||||
|
* <constructor-arg><value>ou=groups</value></constructor-arg>
|
||||||
|
* <property name="groupRoleAttribute"><value>ou</value></property>
|
||||||
|
* </bean>
|
||||||
|
* </constructor-arg>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* <p>This would set up the provider to access an LDAP server with URL
|
||||||
* <tt>ldap://monkeymachine:389/dc=acegisecurity,dc=org</tt>. Authentication will be performed by attempting to bind
|
* <tt>ldap://monkeymachine:389/dc=acegisecurity,dc=org</tt>. Authentication will be performed by attempting to bind
|
||||||
* with the DN <tt>uid=<user-login-name>,ou=people,dc=acegisecurity,dc=org</tt>. After successful
|
* with the DN <tt>uid=<user-login-name>,ou=people,dc=acegisecurity,dc=org</tt>. After successful
|
||||||
* authentication, roles will be assigned to the user by searching under the DN
|
* authentication, roles will be assigned to the user by searching under the DN
|
||||||
@ -90,6 +110,9 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
|||||||
private LdapAuthenticator authenticator;
|
private LdapAuthenticator authenticator;
|
||||||
private LdapAuthoritiesPopulator authoritiesPopulator;
|
private LdapAuthoritiesPopulator authoritiesPopulator;
|
||||||
|
|
||||||
|
/** The provider will reject an authentication request with an empty password if this is set to "true" */
|
||||||
|
private boolean allowEmptyPasswords = true;
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
|
public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||||
@ -111,6 +134,15 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the provider will reject empty passwords by default.
|
||||||
|
* This may be useful when using LDAP servers which interpret an empty password as
|
||||||
|
* anonymous access, even if a (possibly non-existent) principal is supplied.
|
||||||
|
*/
|
||||||
|
public void setAllowEmptyPasswords(boolean allowEmptyPasswords) {
|
||||||
|
this.allowEmptyPasswords = allowEmptyPasswords;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the final <tt>UserDetails</tt> object that will be returned by the provider once the user has
|
* Creates the final <tt>UserDetails</tt> object that will be returned by the provider once the user has
|
||||||
* been authenticated.<p>The <tt>LdapAuthoritiesPopulator</tt> will be used to create the granted
|
* been authenticated.<p>The <tt>LdapAuthoritiesPopulator</tt> will be used to create the granted
|
||||||
@ -157,6 +189,12 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
|||||||
String password = (String) authentication.getCredentials();
|
String password = (String) authentication.getCredentials();
|
||||||
Assert.notNull(password, "Null password was supplied in authentication token");
|
Assert.notNull(password, "Null password was supplied in authentication token");
|
||||||
|
|
||||||
|
if(!allowEmptyPasswords && password.length() == 0) {
|
||||||
|
logger.debug("Rejecting empty password for user " + username);
|
||||||
|
throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyPassword",
|
||||||
|
"Empty Password"));
|
||||||
|
}
|
||||||
|
|
||||||
LdapUserDetails ldapUser = authenticator.authenticate(username, password);
|
LdapUserDetails ldapUser = authenticator.authenticate(username, password);
|
||||||
|
|
||||||
return createUserDetails(ldapUser, username, password);
|
return createUserDetails(ldapUser, username, password);
|
||||||
|
@ -38,6 +38,7 @@ SwitchUserProcessingFilter.expired=User account has expired
|
|||||||
SwitchUserProcessingFilter.credentialsExpired=User credentials have expired
|
SwitchUserProcessingFilter.credentialsExpired=User credentials have expired
|
||||||
AbstractAccessDecisionManager.accessDenied=Access is denied
|
AbstractAccessDecisionManager.accessDenied=Access is denied
|
||||||
LdapAuthenticationProvider.emptyUsername=Empty username not allowed
|
LdapAuthenticationProvider.emptyUsername=Empty username not allowed
|
||||||
|
LdapAuthenticationProvider.emptyPassword=Bad credentials
|
||||||
DefaultIntitalDirContextFactory.communicationFailure=Unable to connect to LDAP server
|
DefaultIntitalDirContextFactory.communicationFailure=Unable to connect to LDAP server
|
||||||
DefaultIntitalDirContextFactory.badCredentials=Bad credentials
|
DefaultIntitalDirContextFactory.badCredentials=Bad credentials
|
||||||
DefaultIntitalDirContextFactory.unexpectedException=Failed to obtain InitialDirContext due to unexpected exception
|
DefaultIntitalDirContextFactory.unexpectedException=Failed to obtain InitialDirContext due to unexpected exception
|
||||||
|
@ -86,6 +86,23 @@ public class LdapAuthenticationProviderTests extends TestCase {
|
|||||||
} catch (BadCredentialsException expected) {}
|
} catch (BadCredentialsException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEmptyPasswordIsAcceptedByDefault() {
|
||||||
|
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||||
|
new MockAuthoritiesPopulator());
|
||||||
|
ldapProvider.retrieveUser("jen", new UsernamePasswordAuthenticationToken("jen", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmptyPasswordIsRejectedWhenFlagIsSet() {
|
||||||
|
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||||
|
new MockAuthoritiesPopulator());
|
||||||
|
ldapProvider.setAllowEmptyPasswords(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ldapProvider.retrieveUser("jen", new UsernamePasswordAuthenticationToken("jen", ""));
|
||||||
|
fail("Expected BadCredentialsException for empty password");
|
||||||
|
} catch (BadCredentialsException expected) {}
|
||||||
|
}
|
||||||
|
|
||||||
public void testNormalUsage() {
|
public void testNormalUsage() {
|
||||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||||
new MockAuthoritiesPopulator());
|
new MockAuthoritiesPopulator());
|
||||||
@ -114,17 +131,23 @@ public class LdapAuthenticationProviderTests extends TestCase {
|
|||||||
Attributes userAttributes = new BasicAttributes("cn", "bob");
|
Attributes userAttributes = new BasicAttributes("cn", "bob");
|
||||||
|
|
||||||
public LdapUserDetails authenticate(String username, String password) {
|
public LdapUserDetails authenticate(String username, String password) {
|
||||||
|
LdapUserDetailsImpl.Essence userEssence = new LdapUserDetailsImpl.Essence();
|
||||||
|
userEssence.setPassword("{SHA}anencodedpassword");
|
||||||
|
userEssence.setAttributes(userAttributes);
|
||||||
|
|
||||||
if (username.equals("bob") && password.equals("bobspassword")) {
|
if (username.equals("bob") && password.equals("bobspassword")) {
|
||||||
LdapUserDetailsImpl.Essence userEssence = new LdapUserDetailsImpl.Essence();
|
|
||||||
userEssence.setDn("cn=bob,ou=people,dc=acegisecurity,dc=org");
|
userEssence.setDn("cn=bob,ou=people,dc=acegisecurity,dc=org");
|
||||||
userEssence.setPassword("{SHA}anencodedpassword");
|
userEssence.addAuthority(new GrantedAuthorityImpl("ROLE_FROM_ENTRY"));
|
||||||
userEssence.setAttributes(userAttributes);
|
|
||||||
|
return userEssence.createUserDetails();
|
||||||
|
} else if (username.equals("jen") && password.equals("")) {
|
||||||
|
userEssence.setDn("cn=jen,ou=people,dc=acegisecurity,dc=org");
|
||||||
userEssence.addAuthority(new GrantedAuthorityImpl("ROLE_FROM_ENTRY"));
|
userEssence.addAuthority(new GrantedAuthorityImpl("ROLE_FROM_ENTRY"));
|
||||||
|
|
||||||
return userEssence.createUserDetails();
|
return userEssence.createUserDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new BadCredentialsException("Authentication of Bob failed.");
|
throw new BadCredentialsException("Authentication failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user