You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by er...@apache.org on 2007/05/08 05:42:28 UTC
svn commit: r536046 - in
/directory/apacheds/branches/kerberos-encryption-types:
kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/
protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/
p...
Author: erodriguez
Date: Mon May 7 20:42:27 2007
New Revision: 536046
URL: http://svn.apache.org/viewvc?view=rev&rev=536046
Log:
New PasswordPolicyService interceptor in 'kerberos-encryption-types' branch:
o Converted Change Password protocol's password policy processor to a core interceptor.
o Added integration test demonstrating password policy enforcement with LDAP protocol.
o Disabled password policy checking in Change Password in lieu of password policy enforcement now in core interceptor.
o Changed Change Password exception handling to propagate password policy error messages from core out to Change Password clients.
o Added PasswordPolicyService interceptor to service chain in server-main's server.xml.
Added:
directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java (with props)
directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java (with props)
Modified:
directory/apacheds/branches/kerberos-encryption-types/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java
directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordChain.java
directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ProcessPasswordChange.java
directory/apacheds/branches/kerberos-encryption-types/server-main/server.xml
Modified: directory/apacheds/branches/kerberos-encryption-types/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java?view=diff&rev=536046&r1=536045&r2=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java (original)
+++ directory/apacheds/branches/kerberos-encryption-types/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java Mon May 7 20:42:27 2007
@@ -68,7 +68,7 @@
}
- public Object execute( DirContext ctx, Name searchBaseDn )
+ public Object execute( DirContext ctx, Name searchBaseDn ) throws NamingException
{
if ( principal == null )
{
@@ -83,16 +83,9 @@
String dn = null;
- try
- {
- dn = search( ctx, principal.getName() );
- Name rdn = getRelativeName( ctx.getNameInNamespace(), dn );
- ctx.modifyAttributes( rdn, mods );
- }
- catch ( NamingException e )
- {
- return null;
- }
+ dn = search( ctx, principal.getName() );
+ Name rdn = getRelativeName( ctx.getNameInNamespace(), dn );
+ ctx.modifyAttributes( rdn, mods );
return dn;
}
Modified: directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordChain.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordChain.java?view=diff&rev=536046&r1=536045&r2=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordChain.java (original)
+++ directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordChain.java Mon May 7 20:42:27 2007
@@ -60,7 +60,6 @@
addLast( "monitorContext", new MonitorContext() );
}
- addLast( "checkPasswordPolicy", new CheckPasswordPolicy() );
addLast( "processPasswordChange", new ProcessPasswordChange() );
addLast( "buildReply", new BuildReply() );
Modified: directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ProcessPasswordChange.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ProcessPasswordChange.java?view=diff&rev=536046&r1=536045&r2=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ProcessPasswordChange.java (original)
+++ directory/apacheds/branches/kerberos-encryption-types/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ProcessPasswordChange.java Mon May 7 20:42:27 2007
@@ -20,6 +20,7 @@
package org.apache.directory.server.changepw.service;
+import javax.naming.NamingException;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
@@ -33,6 +34,8 @@
/**
+ * An {@link IoHandlerCommand} for storing the new password.
+ *
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
@@ -57,15 +60,19 @@
// seq-number must have same value as authenticator
// ignore r-address
- // store password in database
try
{
String principalName = store.changePassword( clientPrincipal, newPassword );
log.debug( "Successfully modified principal {}", principalName );
}
+ catch ( NamingException ne )
+ {
+ log.warn( ne.getMessage(), ne );
+ throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, ne.getExplanation().getBytes() );
+ }
catch ( Exception e )
{
- log.error( e.getMessage(), e );
+ log.error( "Unexpected exception.", e );
throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_HARDERROR );
}
Added: directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java?view=auto&rev=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java (added)
+++ directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java Mon May 7 20:42:27 2007
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.directory.server.kerberos.shared.interceptors;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.OperationContext;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link Interceptor} that enforces password policy for users. Add or modify operations
+ * on the 'userPassword' attribute are checked against a password policy. The password is
+ * rejected if it does not pass the password policy checks. The password MUST be passed to
+ * the core as plaintext.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PasswordPolicyService extends BaseInterceptor
+{
+ /** The log for this class. */
+ private static final Logger log = LoggerFactory.getLogger( PasswordPolicyService.class );
+
+ /** The service name. */
+ public static final String NAME = "passwordPolicyService";
+
+
+ /**
+ * Check added attributes for a 'userPassword'. If a 'userPassword' is found, apply any
+ * password policy checks.
+ */
+ public void add( NextInterceptor next, OperationContext addContext ) throws NamingException
+ {
+ LdapDN normName = addContext.getDn();
+
+ Attributes entry = ( ( AddOperationContext ) addContext ).getEntry();
+
+ log.debug( "Adding the entry " + AttributeUtils.toString( entry ) + " for DN = '" + normName.getUpName() + "'" );
+
+ Object attr = null;
+
+ if ( entry.get( "userPassword" ) != null )
+ {
+ String userPassword = "";
+ String username = "";
+
+ attr = entry.get( "userPassword" ).get();
+
+ if ( attr instanceof String )
+ {
+ log.debug( "Adding Attribute id : 'userPassword', Values : ['" + attr + "']" );
+ userPassword = ( String ) attr;
+ }
+ else if ( attr instanceof byte[] )
+ {
+ String string = StringTools.utf8ToString( ( byte[] ) attr );
+
+ StringBuffer sb = new StringBuffer();
+ sb.append( "'" + string + "' ( " );
+ sb.append( StringTools.dumpBytes( ( byte[] ) attr ).trim() );
+ log.debug( "Adding Attribute id : 'userPassword', Values : [ " + sb.toString() + " ) ]" );
+
+ userPassword = string;
+ }
+
+ if ( entry.get( "cn" ) != null )
+ {
+ attr = entry.get( "cn" ).get();
+ username = ( String ) attr;
+ }
+
+ // If userPassword fails checks, throw new NamingException.
+ check( username, userPassword );
+ }
+
+ next.add( addContext );
+ }
+
+
+ /**
+ * Check modification items for a 'userPassword'. If a 'userPassword' is found, apply any
+ * password policy checks.
+ */
+ public void modify( NextInterceptor next, OperationContext opContext ) throws NamingException
+ {
+ LdapDN name = opContext.getDn();
+ ModifyOperationContext modContext = ( ModifyOperationContext ) opContext;
+
+ ModificationItemImpl[] mods = modContext.getModItems();
+
+ String operation = null;
+
+ for ( int ii = 0; ii < mods.length; ii++ )
+ {
+ switch ( mods[ii].getModificationOp() )
+ {
+ case DirContext.ADD_ATTRIBUTE:
+ operation = "Adding";
+ break;
+ case DirContext.REMOVE_ATTRIBUTE:
+ operation = "Removing";
+ break;
+ case DirContext.REPLACE_ATTRIBUTE:
+ operation = "Replacing";
+ break;
+ }
+
+ Attribute attr = mods[ii].getAttribute();
+ String id = attr.getID();
+
+ if ( id.equalsIgnoreCase( "userPassword" ) )
+ {
+ Object userPassword = attr.get();
+
+ if ( userPassword != null )
+ {
+ if ( userPassword instanceof String )
+ {
+ log.debug( "Adding Attribute id : 'userPassword', Values : ['" + attr + "']" );
+ }
+ else if ( userPassword instanceof byte[] )
+ {
+ String string = StringTools.utf8ToString( ( byte[] ) userPassword );
+
+ StringBuffer sb = new StringBuffer();
+ sb.append( "'" + string + "' ( " );
+ sb.append( StringTools.dumpBytes( ( byte[] ) userPassword ).trim() );
+ log.debug( "Adding Attribute id : 'userPassword', Values : [ " + sb.toString() + " ) ]" );
+
+ userPassword = string;
+ }
+
+ // if userPassword fails checks, throw new NamingException.
+ check( name.getUpName(), ( String ) userPassword );
+ }
+ }
+
+ log.debug( operation + " for entry '" + name.getUpName() + "' the attribute " + mods[ii].getAttribute() );
+ }
+
+ next.modify( opContext );
+ }
+
+
+ void check( String username, String password ) throws NamingException
+ {
+ int passwordLength = 6;
+ int categoryCount = 2;
+ int tokenSize = 3;
+
+ if ( !isValid( username, password, passwordLength, categoryCount, tokenSize ) )
+ {
+ String explanation = buildErrorMessage( username, password, passwordLength, categoryCount, tokenSize );
+ log.error( explanation );
+
+ throw new NamingException( explanation );
+ }
+ }
+
+
+ /**
+ * Tests that:
+ * The password is at least six characters long.
+ * The password contains a mix of characters.
+ * The password does not contain three letter (or more) tokens from the user's account name.
+ */
+ boolean isValid( String username, String password, int passwordLength, int categoryCount, int tokenSize )
+ {
+ return isValidPasswordLength( password, passwordLength ) && isValidCategoryCount( password, categoryCount )
+ && isValidUsernameSubstring( username, password, tokenSize );
+ }
+
+
+ /**
+ * The password is at least six characters long.
+ */
+ boolean isValidPasswordLength( String password, int passwordLength )
+ {
+ return password.length() >= passwordLength;
+ }
+
+
+ /**
+ * The password contains characters from at least three of the following four categories:
+ * English uppercase characters (A - Z)
+ * English lowercase characters (a - z)
+ * Base 10 digits (0 - 9)
+ * Any non-alphanumeric character (for example: !, $, #, or %)
+ */
+ boolean isValidCategoryCount( String password, int categoryCount )
+ {
+ int uppercase = 0;
+ int lowercase = 0;
+ int digit = 0;
+ int nonAlphaNumeric = 0;
+
+ char[] characters = password.toCharArray();
+
+ for ( int ii = 0; ii < characters.length; ii++ )
+ {
+ if ( Character.isLowerCase( characters[ii] ) )
+ {
+ lowercase = 1;
+ }
+ else
+ {
+ if ( Character.isUpperCase( characters[ii] ) )
+ {
+ uppercase = 1;
+ }
+ else
+ {
+ if ( Character.isDigit( characters[ii] ) )
+ {
+ digit = 1;
+ }
+ else
+ {
+ if ( !Character.isLetterOrDigit( characters[ii] ) )
+ {
+ nonAlphaNumeric = 1;
+ }
+ }
+ }
+ }
+ }
+
+ return ( uppercase + lowercase + digit + nonAlphaNumeric ) >= categoryCount;
+ }
+
+
+ /**
+ * The password does not contain three letter (or more) tokens from the user's account name.
+ *
+ * If the account name is less than three characters long, this check is not performed
+ * because the rate at which passwords would be rejected is too high. For each token that is
+ * three or more characters long, that token is searched for in the password; if it is present,
+ * the password change is rejected. For example, the name "First M. Last" would be split into
+ * three tokens: "First", "M", and "Last". Because the second token is only one character long,
+ * it would be ignored. Therefore, this user could not have a password that included either
+ * "first" or "last" as a substring anywhere in the password. All of these checks are
+ * case-insensitive.
+ */
+ boolean isValidUsernameSubstring( String username, String password, int tokenSize )
+ {
+ String[] tokens = username.split( "[^a-zA-Z]" );
+
+ for ( int ii = 0; ii < tokens.length; ii++ )
+ {
+ if ( tokens[ii].length() >= tokenSize )
+ {
+ if ( password.matches( "(?i).*" + tokens[ii] + ".*" ) )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ private String buildErrorMessage( String username, String password, int passwordLength, int categoryCount,
+ int tokenSize )
+ {
+ List<String> violations = new ArrayList<String>();
+
+ if ( !isValidPasswordLength( password, passwordLength ) )
+ {
+ violations.add( "length too short" );
+ }
+
+ if ( !isValidCategoryCount( password, categoryCount ) )
+ {
+ violations.add( "insufficient character mix" );
+ }
+
+ if ( !isValidUsernameSubstring( username, password, tokenSize ) )
+ {
+ violations.add( "contains portions of username" );
+ }
+
+ StringBuffer sb = new StringBuffer( "Password violates policy: " );
+
+ boolean isFirst = true;
+
+ for ( String violation : violations )
+ {
+ if ( isFirst )
+ {
+ isFirst = false;
+ }
+ else
+ {
+ sb.append( ", " );
+ }
+
+ sb.append( violation );
+ }
+
+ return sb.toString();
+ }
+}
Propchange: directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/PasswordPolicyService.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: directory/apacheds/branches/kerberos-encryption-types/server-main/server.xml
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/server-main/server.xml?view=diff&rev=536046&r1=536045&r2=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/server-main/server.xml (original)
+++ directory/apacheds/branches/kerberos-encryption-types/server-main/server.xml Mon May 7 20:42:27 2007
@@ -153,6 +153,12 @@
</property>
</bean>
<bean class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+ <property name="name" value="passwordPolicyService" />
+ <property name="interceptor">
+ <bean class="org.apache.directory.server.kerberos.shared.interceptors.PasswordPolicyService" />
+ </property>
+ </bean>
+ <bean class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
<property name="name" value="keyDerivationService" />
<property name="interceptor">
<bean class="org.apache.directory.server.kerberos.shared.interceptors.KeyDerivationService" />
Added: directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java?view=auto&rev=536046
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java (added)
+++ directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java Mon May 7 20:42:27 2007
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.directory.server;
+
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.directory.server.core.configuration.InterceptorConfiguration;
+import org.apache.directory.server.core.configuration.MutableInterceptorConfiguration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.PartitionConfiguration;
+import org.apache.directory.server.kerberos.shared.interceptors.PasswordPolicyService;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * An {@link AbstractServerTest} testing the (@link {@link PasswordPolicyService}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PasswordPolicyServiceTest extends AbstractServerTest
+{
+ private DirContext ctx = null;
+ private DirContext users = null;
+
+
+ /**
+ * Set up a partition for EXAMPLE.COM, add the {@link PasswordPolicyService}
+ * interceptor, and create a users subcontext.
+ */
+ public void setUp() throws Exception
+ {
+ configuration.setAllowAnonymousAccess( false );
+
+ Attributes attrs;
+ Set<PartitionConfiguration> pcfgs = new HashSet<PartitionConfiguration>();
+
+ MutablePartitionConfiguration pcfg;
+
+ // Add partition 'example'
+ pcfg = new MutablePartitionConfiguration();
+ pcfg.setName( "example" );
+ pcfg.setSuffix( "dc=example,dc=com" );
+
+ Set<Object> indexedAttrs = new HashSet<Object>();
+ indexedAttrs.add( "ou" );
+ indexedAttrs.add( "dc" );
+ indexedAttrs.add( "objectClass" );
+ pcfg.setIndexedAttributes( indexedAttrs );
+
+ attrs = new AttributesImpl( true );
+ Attribute attr = new AttributeImpl( "objectClass" );
+ attr.add( "top" );
+ attr.add( "domain" );
+ attrs.put( attr );
+ attr = new AttributeImpl( "dc" );
+ attr.add( "example" );
+ attrs.put( attr );
+ pcfg.setContextEntry( attrs );
+
+ pcfgs.add( pcfg );
+ configuration.setPartitionConfigurations( pcfgs );
+
+ MutableInterceptorConfiguration interceptorCfg = new MutableInterceptorConfiguration();
+ List<InterceptorConfiguration> list = configuration.getInterceptorConfigurations();
+
+ interceptorCfg.setName( "passwordPolicyService" );
+ interceptorCfg.setInterceptor( new PasswordPolicyService() );
+ list.add( interceptorCfg );
+ configuration.setInterceptorConfigurations( list );
+
+ super.setUp();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+ env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+ env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/dc=example,dc=com" );
+ env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+ env.put( "java.naming.security.credentials", "secret" );
+ env.put( "java.naming.security.authentication", "simple" );
+ ctx = new InitialDirContext( env );
+
+ attrs = getOrgUnitAttributes( "users" );
+ users = ctx.createSubcontext( "ou=users", attrs );
+ }
+
+
+ /**
+ * Tests that passwords that are too short are properly rejected.
+ */
+ public void testLength()
+ {
+ Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "HN1" );
+ try
+ {
+ users.createSubcontext( "uid=hnelson", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertTrue( ne.getMessage().contains( "length too short" ) );
+ assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+ assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords with insufficient character mix are properly rejected.
+ */
+ public void testCharacterMix()
+ {
+ Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret" );
+ try
+ {
+ users.createSubcontext( "uid=hnelson", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertFalse( ne.getMessage().contains( "length too short" ) );
+ assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+ assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords that contain substrings of the username are properly rejected.
+ */
+ public void testContainsUsername()
+ {
+ Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "A1nelson" );
+ try
+ {
+ users.createSubcontext( "uid=hnelson", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertFalse( ne.getMessage().contains( "length too short" ) );
+ assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+ assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords with insufficient character mix and that are too
+ * short are properly rejected.
+ */
+ public void testCharacterMixAndLength()
+ {
+ Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hi" );
+ try
+ {
+ users.createSubcontext( "uid=hnelson", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertTrue( ne.getMessage().contains( "length too short" ) );
+ assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+ assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords that are too short and that contain substrings of
+ * the username are properly rejected.
+ */
+ public void testLengthAndContainsUsername()
+ {
+ Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush1" );
+ try
+ {
+ users.createSubcontext( "uid=wbush", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertTrue( ne.getMessage().contains( "length too short" ) );
+ assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+ assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords with insufficient character mix and that contain substrings of
+ * the username are properly rejected.
+ */
+ public void testCharacterMixAndContainsUsername()
+ {
+ Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hnelson" );
+ try
+ {
+ users.createSubcontext( "uid=hnelson", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertFalse( ne.getMessage().contains( "length too short" ) );
+ assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+ assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tests that passwords with insufficient character mix and that are too
+ * short and that contain substrings of the username are properly rejected.
+ */
+ public void testCharacterMixAndLengthAndContainsUsername()
+ {
+ Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush" );
+ try
+ {
+ users.createSubcontext( "uid=wbush", attrs );
+ fail( "Shouldn't have gotten here." );
+ }
+ catch ( NamingException ne )
+ {
+ assertTrue( ne.getMessage().contains( "length too short" ) );
+ assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+ assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+ }
+ }
+
+
+ /**
+ * Tear down.
+ */
+ public void tearDown() throws Exception
+ {
+ ctx.close();
+ ctx = null;
+ super.tearDown();
+ }
+
+
+ /**
+ * Convenience method for creating a person.
+ */
+ protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword )
+ {
+ Attributes attrs = new AttributesImpl();
+ Attribute ocls = new AttributeImpl( "objectClass" );
+ ocls.add( "top" );
+ ocls.add( "person" ); // sn $ cn
+ ocls.add( "inetOrgPerson" ); // uid
+ attrs.put( ocls );
+ attrs.put( "cn", cn );
+ attrs.put( "sn", sn );
+ attrs.put( "uid", uid );
+ attrs.put( "userPassword", userPassword );
+
+ return attrs;
+ }
+
+
+ /**
+ * Convenience method for creating an organizational unit.
+ */
+ protected Attributes getOrgUnitAttributes( String ou )
+ {
+ Attributes attrs = new AttributesImpl();
+ Attribute ocls = new AttributeImpl( "objectClass" );
+ ocls.add( "top" );
+ ocls.add( "organizationalUnit" );
+ attrs.put( ocls );
+ attrs.put( "ou", ou );
+
+ return attrs;
+ }
+}
Propchange: directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceTest.java
------------------------------------------------------------------------------
svn:eol-style = native