mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Chris Ridd
08.11.2012 739cc9fe3921b9f7f4f582980f61078a1c35eb33
Fix OPENDJ-620 Enhance character set password validator to understand classes like 'All non-Latin characters'
6 files modified
556 ■■■■ changed files
opends/resource/schema/02-config.ldif 12 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml 93 ●●●● patch | view | raw | blame | history
opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties 17 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/extension.properties 32 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java 207 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java 195 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -3333,6 +3333,11 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.62
  NAME 'ds-cfg-character-set-ranges'
  EQUALITY caseExactMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -4304,9 +4309,10 @@
  NAME 'ds-cfg-character-set-password-validator'
  SUP ds-cfg-password-validator
  STRUCTURAL
  MUST ( ds-cfg-character-set $
         ds-cfg-allow-unclassified-characters )
  MAY ds-cfg-min-character-sets
  MUST ds-cfg-allow-unclassified-characters
  MAY ( ds-cfg-min-character-sets $
        ds-cfg-character-set $
        ds-cfg-character-set-ranges )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.97
  NAME 'ds-task-rebuild'
opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
@@ -24,7 +24,7 @@
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Portions copyright 2011 ForgeRock AS
  !      Portions Copyright 2011-2012 ForgeRock AS
  ! -->
<adm:managed-object name="character-set-password-validator"
  plural-name="character-set-password-validators"
@@ -36,7 +36,7 @@
    <adm:user-friendly-name />
    determines whether a proposed password is acceptable by
    checking whether it contains a sufficient number of characters
    from one or more user-defined character sets.
    from one or more user-defined character sets and ranges.
  </adm:synopsis>
  <adm:description>
    For example, 
@@ -44,6 +44,18 @@
    have at least one lowercase letter, one uppercase letter, one digit,
    and one symbol.
  </adm:description>
  <adm:constraint>
    <adm:synopsis>
      The <adm:user-friendly-name/> must have at least one character set
      or range specified.
    </adm:synopsis>
    <adm:condition>
      <adm:or>
        <adm:is-present property="character-set" />
        <adm:is-present property="character-set-ranges" />
      </adm:or>
    </adm:condition>
  </adm:constraint>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-character-set-password-validator</ldap:name>
@@ -59,7 +71,7 @@
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="character-set" mandatory="true"
  <adm:property name="character-set" mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      Specifies a character set containing characters that a password
@@ -76,6 +88,14 @@
      character sets can be defined in separate values, although no
      character can appear in more than one character set.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          If no sets are specified, the validator only uses the
          defined character ranges.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string case-insensitive="false" />
    </adm:syntax>
@@ -85,16 +105,51 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="character-set-ranges" mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      Specifies a character range containing characters that a password
      may contain and a value indicating the minimum number of
      characters required from that range.
    </adm:synopsis>
    <adm:description>
      Each value must be an integer (indicating the minimum required
      characters from the range which may be zero, indicating that the
      character range is optional) followed by a colon and one or more
      range specifications. A range specification is 3 characters: the
      first character allowed, a minus, and the last character allowed.
      For example, "3:A-Za-z0-9". The ranges in each value should not
      overlap, and the characters in each range specification should be
      ordered.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          If no ranges are specified, the validator only uses the
          defined character sets.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string case-insensitive="false" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-character-set-ranges</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allow-unclassified-characters" mandatory="true">
    <adm:synopsis>
      Indicates whether this password validator allows passwords to
      contain characters outside of any of the user-defined character
      sets.
      sets and ranges.
    </adm:synopsis>
    <adm:description>
      If this is "false", then only those characters in the user-defined
      character sets may be used in passwords. Any password containing a
      character not included in any character set will be rejected.
      character sets and ranges may be used in passwords. Any password
      containing a  character not included in any character set or range
      will be rejected.
    </adm:description>
    <adm:syntax>
      <adm:boolean />
@@ -107,26 +162,28 @@
  </adm:property>
  <adm:property name="min-character-sets" mandatory="false">
    <adm:synopsis>
      Specifies the minimum number of character sets that a password must
      contain.
      Specifies the minimum number of character sets and ranges that a
      password must contain.
    </adm:synopsis>
    <adm:description>
      This property should only be used in conjunction with optional character
      sets (those requiring zero characters). Its value must include any
      mandatory character sets (those requiring great than zero characters).
      This is useful in situations where a password must contain characters
      from mandatory character sets, and characters from at least N optional
      character sets. For example, it is quite common to require that a
      password contains at least one non-alphanumeric character as well as
      characters from two alphanumeric character sets (lower-case,
      upper-case, digits). In this case, this property should be set to 3.
      sets and ranges (those requiring zero characters). Its value must
      include any mandatory character sets and ranges (those requiring greater
      than zero characters). This is useful in situations where a password
      must contain characters from mandatory character sets and ranges, and
      characters from at least N optional character sets and ranges. For
      example, it is quite common to require that a password contains at
      least one non-alphanumeric character as well as characters from two
      alphanumeric character sets (lower-case, upper-case, digits). In this
      case, this property should be set to 3.
    </adm:description>
  <adm:default-behavior>
    <adm:alias>
      <adm:synopsis>
        The password must contain characters from each of the mandatory
        character sets and, if there are optional character sets, at least
        one character from one of the optional character sets.
        character sets and ranges and, if there are optional character sets
        and ranges, at least one character from one of the optional character
        sets and ranges.
      </adm:synopsis>
    </adm:alias>
  </adm:default-behavior>
opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties
@@ -1,13 +1,18 @@
user-friendly-name=Character Set Password Validator
user-friendly-plural-name=Character Set Password Validators
synopsis=The Character Set Password Validator determines whether a proposed password is acceptable by checking whether it contains a sufficient number of characters from one or more user-defined character sets.
synopsis=The Character Set Password Validator determines whether a proposed password is acceptable by checking whether it contains a sufficient number of characters from one or more user-defined character sets and ranges.
description=For example, the validator can ensure that passwords must have at least one lowercase letter, one uppercase letter, one digit, and one symbol.
property.allow-unclassified-characters.synopsis=Indicates whether this password validator allows passwords to contain characters outside of any of the user-defined character sets.
property.allow-unclassified-characters.description=If this is "false", then only those characters in the user-defined character sets may be used in passwords. Any password containing a character not included in any character set will be rejected.
constraint.1.synopsis=The Character Set Password Validator must have at least one character set or range specified.
property.allow-unclassified-characters.synopsis=Indicates whether this password validator allows passwords to contain characters outside of any of the user-defined character sets and ranges.
property.allow-unclassified-characters.description=If this is "false", then only those characters in the user-defined character sets and ranges may be used in passwords. Any password containing a character not included in any character set or range will be rejected.
property.character-set.synopsis=Specifies a character set containing characters that a password may contain and a value indicating the minimum number of characters required from that set.
property.character-set.description=Each value must be an integer (indicating the minimum required characters from the set which may be zero, indicating that the character set is optional) followed by a colon and the characters to include in that set (for example, "3:abcdefghijklmnopqrstuvwxyz" indicates that a user password must contain at least three characters from the set of lowercase ASCII letters). Multiple character sets can be defined in separate values, although no character can appear in more than one character set.
property.character-set.default-behavior.alias.synopsis=If no sets are specified, the validator only uses the defined character ranges.
property.character-set-ranges.synopsis=Specifies a character range containing characters that a password may contain and a value indicating the minimum number of characters required from that range.
property.character-set-ranges.description=Each value must be an integer (indicating the minimum required characters from the range which may be zero, indicating that the character range is optional) followed by a colon and one or more range specifications. A range specification is 3 characters: the first character allowed, a minus, and the last character allowed. For example, "3:A-Za-z0-9". The ranges in each value should not overlap, and the characters in each range specification should be ordered.
property.character-set-ranges.default-behavior.alias.synopsis=If no ranges are specified, the validator only uses the defined character sets.
property.enabled.synopsis=Indicates whether the password validator is enabled for use.
property.java-class.synopsis=Specifies the fully-qualified name of the Java class that provides the password validator implementation.
property.min-character-sets.synopsis=Specifies the minimum number of character sets that a password must contain.
property.min-character-sets.description=This property should only be used in conjunction with optional character sets (those requiring zero characters). Its value must include any mandatory character sets (those requiring great than zero characters). This is useful in situations where a password must contain characters from mandatory character sets, and characters from at least N optional character sets. For example, it is quite common to require that a password contains at least one non-alphanumeric character as well as characters from two alphanumeric character sets (lower-case, upper-case, digits). In this case, this property should be set to 3.
property.min-character-sets.default-behavior.alias.synopsis=The password must contain characters from each of the mandatory character sets and, if there are optional character sets, at least one character from one of the optional character sets.
property.min-character-sets.synopsis=Specifies the minimum number of character sets and ranges that a password must contain.
property.min-character-sets.description=This property should only be used in conjunction with optional character sets and ranges (those requiring zero characters). Its value must include any mandatory character sets and ranges (those requiring greater than zero characters). This is useful in situations where a password must contain characters from mandatory character sets and ranges, and characters from at least N optional character sets and ranges. For example, it is quite common to require that a password contains at least one non-alphanumeric character as well as characters from two alphanumeric character sets (lower-case, upper-case, digits). In this case, this property should be set to 3.
property.min-character-sets.default-behavior.alias.synopsis=The password must contain characters from each of the mandatory character sets and ranges and, if there are optional character sets and ranges, at least one character from one of the optional character sets and ranges.
opends/src/messages/messages/extension.properties
@@ -1136,12 +1136,12 @@
 did not contain enough characters from the character set '%s'.  The minimum \
 number of characters from that set that must be present in user passwords is \
 %d
MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=The provided character set definition \
 '%s' is invalid because it does not contain a colon to separate the minimum \
 count from the character set
MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=The provided character set definition \
MILD_ERR_CHARSET_VALIDATOR_NO_SET_COLON_466=The provided character set \
 definition '%s' is invalid because it does not contain a colon to separate \
 the minimum count from the character set
MILD_ERR_CHARSET_VALIDATOR_NO_SET_CHARS_467=The provided character set definition \
 '%s' is invalid because the provided character set is empty
MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=The provided character set \
MILD_ERR_CHARSET_VALIDATOR_INVALID_SET_COUNT_468=The provided character set \
 definition '%s' is invalid because the value before the colon must be an \
 integer greater or equal to zero
MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=The provided character set \
@@ -1424,7 +1424,7 @@
 the total number of defined character sets
MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_OPTIONAL_CHAR_SETS_581=The provided \
 password did not contain characters from at least %d of the following \
 character sets: %s
 character sets or ranges: %s
MILD_ERR_STATICMEMBERS_CANNOT_DECODE_DN_582=An error occurred while \
 attempting to decode member's DN %s of static group %s:  %s
MILD_ERR_SASL_ACCOUNT_NOT_LOCAL_583=SASL %s authentication \
@@ -1552,4 +1552,22 @@
 searchable and should not be included in otherwise unindexed search filters
MILD_ERR_CHANGELOGBASEDN_VATTR_NOT_SEARCHABLE_628=The %s attribute is not \
 searchable and should not be included in otherwise unindexed search filters
MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_RANGE_629=The provided password \
 did not contain enough characters from the character range '%s'.  The minimum \
 number of characters from that range that must be present in user passwords \
 is %d
MILD_ERR_CHARSET_VALIDATOR_NO_RANGE_COLON_630=The provided character range \
 definition  '%s' is invalid because it does not contain a colon to separate \
 the minimum count from the character range
MILD_ERR_CHARSET_VALIDATOR_NO_RANGE_CHARS_631=The provided character range \
 definition  '%s' is invalid because it does not contain a colon to separate \
 the minimum count from the character range
MILD_ERR_CHARSET_VALIDATOR_INVALID_RANGE_COUNT_632=The provided character \
 range definition '%s' is invalid because the value before the colon must be \
 an integer greater or equal to zero
MILD_ERR_CHARSET_VALIDATOR_UNSORTED_RANGE_633=The provided character range \
 definition '%s' is invalid because the range '%s' is reversed
MILD_ERR_CHARSET_VALIDATOR_MALFORMED_RANGE_634=The provided character range \
 definition '%s' is invalid because the range '%s' is missing the minus
MILD_ERR_CHARSET_VALIDATOR_SHORT_RANGE_635=The provided character range \
 definition '%s' is invalid because the range '%s' is too short
opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions Copyright 2011-2012 ForgeRock AS
 */
package org.opends.server.extensions;
import org.opends.messages.Message;
@@ -64,6 +64,10 @@
  // required for each.
  private HashMap<String,Integer> characterSets;
  // A mapping between the character ranges and the minimum number of characters
  // required for each.
  private HashMap<String,Integer> characterRanges;
  /**
@@ -90,8 +94,9 @@
    configuration.addCharacterSetChangeListener(this);
    currentConfig = configuration;
    // Make sure that each of the character set definitions are acceptable.
    characterSets = processCharacterSets(configuration);
    // Make sure that each of the character set and range definitions are
    // acceptable.
    processCharacterSetsAndRanges(configuration, true);
  }
@@ -123,7 +128,8 @@
    // Process the provided password.
    String password = newPassword.toString();
    HashMap<String,Integer> counts = new HashMap<String,Integer>();
    HashMap<String,Integer> setCounts = new HashMap<String,Integer>();
    HashMap<String,Integer> rangeCounts = new HashMap<String,Integer>();
    for (int i=0; i < password.length(); i++)
    {
      char c = password.charAt(i);
@@ -132,21 +138,47 @@
      {
        if (characterSet.indexOf(c) >= 0)
        {
          Integer count = counts.get(characterSet);
          Integer count = setCounts.get(characterSet);
          if (count == null)
          {
            counts.put(characterSet, 1);
            setCounts.put(characterSet, 1);
          }
          else
          {
            counts.put(characterSet, count+1);
            setCounts.put(characterSet, count+1);
          }
          found = true;
          break;
        }
      }
      if (!found)
      {
        for (String characterRange : characterRanges.keySet())
        {
          int rangeStart = 0;
          while (rangeStart < characterRange.length())
          {
            if (characterRange.charAt(rangeStart) <= c
                && c <= characterRange.charAt(rangeStart+2))
            {
              Integer count = rangeCounts.get(characterRange);
              if (count == null)
              {
                rangeCounts.put(characterRange, 1);
              }
              else
              {
                rangeCounts.put(characterRange, count+1);
              }
              found = true;
              break;
            }
            rangeStart += 3;
          }
        }
      }
      if ((! found) && (! config.isAllowUnclassifiedCharacters()))
      {
        invalidReason.append(ERR_CHARSET_VALIDATOR_ILLEGAL_CHARACTER.get(
@@ -161,7 +193,7 @@
    for (String characterSet : characterSets.keySet())
    {
      int minimumCount = characterSets.get(characterSet);
      Integer passwordCount = counts.get(characterSet);
      Integer passwordCount = setCounts.get(characterSet);
      if (minimumCount > 0)
      {
        // Mandatory character set.
@@ -184,6 +216,33 @@
        }
      }
    }
    for (String characterRange : characterRanges.keySet())
    {
      int minimumCount = characterRanges.get(characterRange);
      Integer passwordCount = rangeCounts.get(characterRange);
      if (minimumCount > 0)
      {
        // Mandatory character set.
        mandatoryCharacterSets++;
        if ((passwordCount == null) || (passwordCount < minimumCount))
        {
          invalidReason
              .append(ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_RANGE
                  .get(characterRange, minimumCount));
          return false;
        }
      }
      else
      {
        // Optional character set.
        optionalCharacterSets++;
        if (passwordCount != null)
        {
          usedOptionalCharacterSets++;
        }
      }
    }
    // Check minimum optional character sets are present.
    if (optionalCharacterSets > 0)
@@ -215,6 +274,19 @@
            builder.append('\'');
          }
        }
        for (String characterRange : characterRanges.keySet())
        {
          if (characterRanges.get(characterRange) == 0)
          {
            if (builder.length() > 0)
            {
              builder.append(", ");
            }
            builder.append('\'');
            builder.append(characterRange);
            builder.append('\'');
          }
        }
        invalidReason
            .append(ERR_CHARSET_VALIDATOR_TOO_FEW_OPTIONAL_CHAR_SETS
@@ -235,21 +307,20 @@
   * definitions and associated minimum counts from them.
   *
   * @param  configuration  the configuration for this password validator.
   *
   * @return  The mapping between strings of character set values and the
   *          minimum number of characters required from those sets.
   *
   * @param  apply <CODE>true</CODE> if the configuration is being applied,
   *         <CODE>false</CODE> if it is just being validated.
   * @throws  ConfigException  If any of the character set definitions cannot be
   *                           parsed, or if there are any characters present in
   *                           multiple sets.
   */
  private HashMap<String,Integer>
               processCharacterSets(
                    CharacterSetPasswordValidatorCfg configuration)
  private void processCharacterSetsAndRanges(
                    CharacterSetPasswordValidatorCfg configuration,
                    boolean apply)
          throws ConfigException
  {
    HashMap<String,Integer> characterSets  = new HashMap<String,Integer>();
    HashSet<Character>      usedCharacters = new HashSet<Character>();
    HashMap<String,Integer> characterSets   = new HashMap<String,Integer>();
    HashMap<String,Integer> characterRanges = new HashMap<String,Integer>();
    HashSet<Character>      usedCharacters  = new HashSet<Character>();
    int mandatoryCharacterSets = 0;
    for (String definition : configuration.getCharacterSet())
@@ -257,12 +328,12 @@
      int colonPos = definition.indexOf(':');
      if (colonPos <= 0)
      {
        Message message = ERR_CHARSET_VALIDATOR_NO_COLON.get(definition);
        Message message = ERR_CHARSET_VALIDATOR_NO_SET_COLON.get(definition);
        throw new ConfigException(message);
      }
      else if (colonPos == (definition.length() - 1))
      {
        Message message = ERR_CHARSET_VALIDATOR_NO_CHARS.get(definition);
        Message message = ERR_CHARSET_VALIDATOR_NO_SET_CHARS.get(definition);
        throw new ConfigException(message);
      }
@@ -273,13 +344,15 @@
      }
      catch (Exception e)
      {
        Message message = ERR_CHARSET_VALIDATOR_INVALID_COUNT.get(definition);
        Message message = ERR_CHARSET_VALIDATOR_INVALID_SET_COUNT
            .get(definition);
        throw new ConfigException(message);
      }
      if (minCount < 0)
      {
        Message message = ERR_CHARSET_VALIDATOR_INVALID_COUNT.get(definition);
        Message message = ERR_CHARSET_VALIDATOR_INVALID_SET_COUNT
            .get(definition);
        throw new ConfigException(message);
      }
@@ -305,8 +378,86 @@
      }
    }
    // Check the ranges
    for (String definition : configuration.getCharacterSetRanges())
    {
      int colonPos = definition.indexOf(':');
      if (colonPos <= 0)
      {
        Message message = ERR_CHARSET_VALIDATOR_NO_RANGE_COLON.get(definition);
        throw new ConfigException(message);
      }
      else if (colonPos == (definition.length() - 1))
      {
        Message message = ERR_CHARSET_VALIDATOR_NO_RANGE_CHARS.get(definition);
        throw new ConfigException(message);
      }
      int minCount;
      try
      {
        minCount = Integer.parseInt(definition.substring(0, colonPos));
      }
      catch (Exception e)
      {
        Message message = ERR_CHARSET_VALIDATOR_INVALID_RANGE_COUNT
            .get(definition);
        throw new ConfigException(message);
      }
      if (minCount < 0)
      {
        Message message = ERR_CHARSET_VALIDATOR_INVALID_RANGE_COUNT
            .get(definition);
        throw new ConfigException(message);
      }
      String characterRange = definition.substring(colonPos+1);
      /*
       * Ensure we have a number of valid range specifications which are
       * each 3 chars long.
       * e.g. "a-zA-Z0-9"
       */
      int rangeOffset = 0;
      while (rangeOffset < characterRange.length())
      {
        if (rangeOffset > characterRange.length() - 3)
        {
          Message message = ERR_CHARSET_VALIDATOR_SHORT_RANGE
              .get(definition, characterRange.substring(rangeOffset));
          throw new ConfigException(message);
        }
        if (characterRange.charAt(rangeOffset+1) != '-')
        {
          Message message = ERR_CHARSET_VALIDATOR_MALFORMED_RANGE
              .get(definition, characterRange
                  .substring(rangeOffset,rangeOffset+3));
          throw new ConfigException(message);
        }
        if (characterRange.charAt(rangeOffset) >=
            characterRange.charAt(rangeOffset+2))
        {
          Message message = ERR_CHARSET_VALIDATOR_UNSORTED_RANGE
              .get(definition, characterRange
                  .substring(rangeOffset, rangeOffset+3));
          throw new ConfigException(message);
        }
        rangeOffset += 3;
      }
      characterRanges.put(characterRange, minCount);
      if (minCount > 0)
      {
        mandatoryCharacterSets++;
      }
    }
    // Validate min-character-sets if necessary.
    int optionalCharacterSets = characterSets.size()
    int optionalCharacterSets = characterSets.size() + characterRanges.size()
        - mandatoryCharacterSets;
    if (optionalCharacterSets > 0
        && configuration.getMinCharacterSets() != null)
@@ -320,7 +471,7 @@
        throw new ConfigException(message);
      }
      if (minCharacterSets > characterSets.size())
      if (minCharacterSets > (characterSets.size() + characterRanges.size()))
      {
        Message message = ERR_CHARSET_VALIDATOR_MIN_CHAR_SETS_TOO_BIG
            .get(minCharacterSets);
@@ -328,7 +479,11 @@
      }
    }
    return characterSets;
    if (apply)
    {
      this.characterSets = characterSets;
      this.characterRanges = characterRanges;
    }
  }
@@ -358,7 +513,7 @@
    // we'll accept the new configuration.
    try
    {
      processCharacterSets(configuration);
      processCharacterSetsAndRanges(configuration, false);
    }
    catch (ConfigException ce)
    {
@@ -386,7 +541,7 @@
    // activate the new configuration.
    try
    {
      characterSets = processCharacterSets(configuration);
      processCharacterSetsAndRanges(configuration, true);
      currentConfig = configuration;
    }
    catch (Exception e)
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions Copyright 2011-2012 ForgeRock AS
 */
package org.opends.server.extensions;
@@ -139,6 +139,31 @@
         "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
         "ds-cfg-character-set: 0:0123456789",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 2",
         "",
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 0:0-9",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 1",
         "",
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:A-Z\u13a0-\u13f4",
         "ds-cfg-character-set: 0:0123456789",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 2");
    Object[][] array = new Object[entries.size()][1];
@@ -324,7 +349,131 @@
         "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
         "ds-cfg-character-set: 0:0123456789",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 3");
         "ds-cfg-min-character-sets: 3",
         "",
         // Malformed character range definition -- no colon.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: malformed",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character range definition -- colon first.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: :malformed",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- colon last.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- non-integer count.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: noninteger:a-z",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- negative count.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: -1:a-z",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- incomplete range.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:a-",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- incomplete first
         // of multiple ranges.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:a-A-Z",
         "ds-cfg-allow-unclassified-characters: true",
         "",
         // Malformed character ranges definition -- incomplete last
         // of multiple ranges.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:a-zA",
         "ds-cfg-allow-unclassified-characters: malformed",
         "",
         // Malformed character ranges definition -- unsorted range.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:z-a",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 0",
         "",
         // Malformed character ranges definition -- no minus.
         "dn: cn=Character Set,cn=Password Validators,cn=config",
         "objectClass: top",
         "objectClass: ds-cfg-password-validator",
         "objectClass: ds-cfg-character-set-password-validator",
         "cn: Character Set",
         "ds-cfg-java-class: org.opends.server.extensions." +
              "CharacterSetPasswordValidator",
         "ds-cfg-enabled: true",
         "ds-cfg-character-set-ranges: 1:a_z",
         "ds-cfg-allow-unclassified-characters: true",
         "ds-cfg-min-character-sets: 1");
    Object[][] array = new Object[entries.size()][1];
    for (int i=0; i < array.length; i++)
@@ -691,6 +840,48 @@
        "abc123ABC",
        true
      },
      // 3 optional ranges (one is Thai), must use two.
      new Object[]
      {
        TestCaseUtils.makeEntry(
             "dn: cn=Character Set,cn=Password Validators,cn=config",
             "objectClass: top",
             "objectClass: ds-cfg-password-validator",
             "objectClass: ds-cfg-character-set-password-validator",
             "cn: Character Set",
             "ds-cfg-java-class: org.opends.server.extensions." +
                  "CharacterSetPasswordValidator",
             "ds-cfg-enabled: true",
             "ds-cfg-character-set-ranges: 0:a-zA-Z",
             "ds-cfg-character-set-ranges: 0:0-9",
             "ds-cfg-character-set-ranges: 0:\u0e01-\u0e5b",
             "ds-cfg-min-character-sets: 2",
             "ds-cfg-allow-unclassified-characters: false"),
        "abcABC",
        false
      },
      // 3 optional ranges (one is Thai), must use two.
      new Object[]
      {
        TestCaseUtils.makeEntry(
             "dn: cn=Character Set,cn=Password Validators,cn=config",
             "objectClass: top",
             "objectClass: ds-cfg-password-validator",
             "objectClass: ds-cfg-character-set-password-validator",
             "cn: Character Set",
             "ds-cfg-java-class: org.opends.server.extensions." +
                  "CharacterSetPasswordValidator",
             "ds-cfg-enabled: true",
             "ds-cfg-character-set-ranges: 0:a-zA-Z",
             "ds-cfg-character-set-ranges: 0:0-9",
             "ds-cfg-character-set-ranges: 0:\u0e01-\u0e5b",
             "ds-cfg-min-character-sets: 2",
             "ds-cfg-allow-unclassified-characters: false"),
        "abc\u0e17ABC",
        true
      },
    };
  }