SEC-214: Add functionality to be able to use LDAP password policy request/response controls. Added PasswordPolicyAwareContextSource, ppolicy control implementations (from Sandbox) and modified BindAuthenticator to check for the presence of the response control, adding the control to the retured DirContextAdapter if appropriate. LdapUserDetailsImpl also contains the data for grace logins remaining and time till password expiry. Added OpenLDAP startup script with test data and integration test which operates against the data (must be run manually).

This commit is contained in:
Luke Taylor
2009-08-18 23:09:16 +00:00
parent 48988bde84
commit 4df370b100
21 changed files with 1194 additions and 22 deletions

View File

@@ -0,0 +1,67 @@
package org.springframework.security.ldap.ppolicy;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
/**
* Test cases which run against an OpenLDAP server.
* <p>
* Run the script in the module root to start the server and import the data before running.
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class OpenLDAPIntegrationTestSuite {
PasswordPolicyAwareContextSource cs;
@Before
public void createContextSource() throws Exception {
cs = new PasswordPolicyAwareContextSource("ldap://localhost:22389/dc=springsource,dc=com");
cs.setUserDn("cn=admin,dc=springsource,dc=com");
cs.setPassword("password");
cs.afterPropertiesSet();
}
@Test
public void simpleBindSucceeds() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
provider.authenticate(new UsernamePasswordAuthenticationToken("luke","password"));
}
@Test(expected=LockedException.class)
public void repeatedBindWithWrongPasswordLocksAccount() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
for (int count=1; count < 4; count++) {
try {
Authentication a = provider.authenticate(new UsernamePasswordAuthenticationToken("lockme","wrong"));
LdapUserDetailsImpl ud = (LdapUserDetailsImpl) a.getPrincipal();
assertTrue(ud.getTimeBeforeExpiration() < Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0);
} catch (BadCredentialsException expected) {
}
}
}
@Test
public void passwordExpiryTimeIsDetectedCorrectly() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
Authentication a = provider.authenticate(new UsernamePasswordAuthenticationToken("expireme","password"));
PasswordPolicyData ud = (LdapUserDetailsImpl) a.getPrincipal();
assertTrue(ud.getTimeBeforeExpiration() < Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0);
}
}

View File

@@ -0,0 +1,67 @@
package org.springframework.security.ldap.ppolicy;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
/**
* Test cases which run against an OpenLDAP server.
* <p>
* Run the script in the module root to start the server and import the data before running.
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class OpenLDAPIntegrationTestSuite {
PasswordPolicyAwareContextSource cs;
@Before
public void createContextSource() throws Exception {
cs = new PasswordPolicyAwareContextSource("ldap://localhost:22389/dc=springsource,dc=com");
cs.setUserDn("cn=admin,dc=springsource,dc=com");
cs.setPassword("password");
cs.afterPropertiesSet();
}
@Test
public void simpleBindSucceeds() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
provider.authenticate(new UsernamePasswordAuthenticationToken("luke","password"));
}
@Test(expected=LockedException.class)
public void repeatedBindWithWrongPasswordLocksAccount() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
for (int count=1; count < 4; count++) {
try {
Authentication a = provider.authenticate(new UsernamePasswordAuthenticationToken("lockme","wrong"));
LdapUserDetailsImpl ud = (LdapUserDetailsImpl) a.getPrincipal();
assertTrue(ud.getTimeBeforeExpiration() < Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0);
} catch (BadCredentialsException expected) {
}
}
}
@Test
public void passwordExpiryTimeIsDetectedCorrectly() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(cs);
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=users"});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
Authentication a = provider.authenticate(new UsernamePasswordAuthenticationToken("expireme","password"));
PasswordPolicyData ud = (LdapUserDetailsImpl) a.getPrincipal();
assertTrue(ud.getTimeBeforeExpiration() < Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0);
}
}

View File

@@ -0,0 +1,124 @@
/* 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.springframework.security.ldap.ppolicy;
import junit.framework.TestCase;
/**
* Tests for <tt>PasswordPolicyResponse</tt>.
*
* @author Luke Taylor
* @version $Id: PasswordPolicyResponseControlTests.java 2217 2007-10-27 00:45:30Z luke_t $
*/
public class PasswordPolicyResponseControlTests extends TestCase {
//~ Methods ========================================================================================================
/**
* Useful method for obtaining data from a server for use in tests
*/
// public void testAgainstServer() throws Exception {
// Hashtable env = new Hashtable();
// env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// env.put(Context.PROVIDER_URL, "ldap://gorille:389/");
// env.put(Context.SECURITY_AUTHENTICATION, "simple");
// env.put(Context.SECURITY_PRINCIPAL, "cn=manager,dc=security,dc=org");
// env.put(Context.SECURITY_CREDENTIALS, "security");
// env.put(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());
//
// InitialLdapContext ctx = new InitialLdapContext(env, null);
//
// Control[] rctls = { new PasswordPolicyControl(false) };
//
// ctx.setRequestControls(rctls);
//
// try {
// ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "uid=bob,ou=people,dc=security,dc=org" );
// ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, "bobspassword");
// Object o = ctx.lookup("");
//
// System.out.println(o);
//
// } catch(NamingException ne) {
// // Ok.
// System.err.println(ne);
// }
//
// PasswordPolicyResponseControl ctrl = getPPolicyResponseCtl(ctx);
// System.out.println(ctrl);
//
// assertNotNull(ctrl);
//
// //com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);
// }
// private PasswordPolicyResponseControl getPPolicyResponseCtl(InitialLdapContext ctx) throws NamingException {
// Control[] ctrls = ctx.getResponseControls();
//
// for (int i = 0; ctrls != null && i < ctrls.length; i++) {
// if (ctrls[i] instanceof PasswordPolicyResponseControl) {
// return (PasswordPolicyResponseControl) ctrls[i];
// }
// }
//
// return null;
// }
public void testOpenLDAP33SecondsTillPasswordExpiryCtrlIsParsedCorrectly() {
byte[] ctrlBytes = {0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA0, 0x1, 0x21};
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
assertTrue(ctrl.hasWarning());
assertEquals(33, ctrl.getTimeBeforeExpiration());
}
public void testOpenLDAP496GraceLoginsRemainingCtrlIsParsedCorrectly() {
byte[] ctrlBytes = {0x30, 0x06, (byte) 0xA0, 0x04, (byte) 0xA1, 0x02, 0x01, (byte) 0xF0};
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
assertTrue(ctrl.hasWarning());
assertEquals(496, ctrl.getGraceLoginsRemaining());
}
public void testOpenLDAP5GraceLoginsRemainingCtrlIsParsedCorrectly() {
byte[] ctrlBytes = {0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA1, 0x01, 0x05};
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
assertTrue(ctrl.hasWarning());
assertEquals(5, ctrl.getGraceLoginsRemaining());
}
public void testOpenLDAPAccountLockedCtrlIsParsedCorrectly() {
byte[] ctrlBytes = {0x30, 0x03, (byte) 0xA1, 0x01, 0x01};
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
assertTrue(ctrl.hasError() && ctrl.isLocked());
assertFalse(ctrl.hasWarning());
}
public void testOpenLDAPPasswordExpiredCtrlIsParsedCorrectly() {
byte[] ctrlBytes = {0x30, 0x03, (byte) 0xA1, 0x01, 0x00};
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
assertTrue(ctrl.hasError() && ctrl.isExpired());
assertFalse(ctrl.hasWarning());
}
}