From 2f8fa14f39d4a2919b36a9d0d96a0237a4531b13 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 01 Jun 2011 10:38:59 +0000
Subject: [PATCH] Fix OPENDJ-168: Enhance character set password validator to support optional character sets

---
 opendj-sdk/opends/src/messages/messages/extension_ko.properties                                                              |    1 
 opendj-sdk/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java                                 |   97 +++++++++++++
 opendj-sdk/opends/src/messages/messages/extension.properties                                                                 |   11 +
 opendj-sdk/opends/src/messages/messages/extension_es.properties                                                              |    1 
 opendj-sdk/opends/src/messages/messages/extension_zh_CN.properties                                                           |    1 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java |  240 ++++++++++++++++++++++++++++++++--
 opendj-sdk/opends/src/messages/messages/extension_ja.properties                                                              |    1 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml                  |   38 +++++
 opendj-sdk/opends/src/messages/messages/extension_de.properties                                                              |    1 
 opendj-sdk/opends/src/messages/messages/extension_fr.properties                                                              |    1 
 opendj-sdk/opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties                                         |    5 
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                             |    6 
 opendj-sdk/opends/src/messages/messages/extension_zh_TW.properties                                                           |    1 
 13 files changed, 374 insertions(+), 30 deletions(-)

diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 4d84b61..13f4a63 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2554,6 +2554,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.5
+  NAME 'ds-cfg-min-character-sets'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -3511,6 +3516,7 @@
   STRUCTURAL
   MUST ( ds-cfg-character-set $
          ds-cfg-allow-unclassified-characters )
+  MAY ds-cfg-min-character-sets
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.97
   NAME 'ds-task-rebuild'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
index 031671d..69f133a 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
@@ -24,6 +24,7 @@
   !
   !
   !      Copyright 2007-2008 Sun Microsystems, Inc.
+  !      Portions copyright 2011 ForgeRock AS
   ! -->
 <adm:managed-object name="character-set-password-validator"
   plural-name="character-set-password-validators"
@@ -67,7 +68,8 @@
     </adm:synopsis>
     <adm:description>
       Each value must be an integer (indicating the minimum required
-      characters from the set) followed by a colon and the characters to
+      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
@@ -103,4 +105,38 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="min-character-sets" mandatory="false">
+    <adm:synopsis>
+      Specifies the minimum number of character sets 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.  
+    </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.
+      </adm:synopsis>
+    </adm:alias>
+  </adm:default-behavior>
+  <adm:syntax>
+      <adm:integer />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-min-character-sets</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties
index 5749635..505703b 100644
--- a/opendj-sdk/opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/CharacterSetPasswordValidatorCfgDefn.properties
@@ -5,6 +5,9 @@
 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.
 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) 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.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.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.
diff --git a/opendj-sdk/opends/src/messages/messages/extension.properties b/opendj-sdk/opends/src/messages/messages/extension.properties
index 763eeb0..ee34cfe 100644
--- a/opendj-sdk/opends/src/messages/messages/extension.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension.properties
@@ -1157,7 +1157,7 @@
  '%s' is invalid because the provided character set is empty
 MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=The provided character set \
  definition '%s' is invalid because the value before the colon must be an \
- integer greater than zero
+ integer greater or equal to zero
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=The provided character set \
  definition '%s' is invalid because it contains character '%s' which has \
  already been used
@@ -1430,3 +1430,12 @@
  unindexed search filters
 MILD_ERR_PWSCHEME_INVALID_BASE64_DECODED_STORED_PASSWORD_578=The password \
 value %s has been base64-decoded but is too short to be valid
+MILD_ERR_CHARSET_VALIDATOR_MIN_CHAR_SETS_TOO_SMALL_579=The provided minimum \
+ required number of character sets '%d' is invalid because it must include \
+ all mandatory character sets and at least one optional character set
+MILD_ERR_CHARSET_VALIDATOR_MIN_CHAR_SETS_TOO_BIG_580=The provided minimum \
+ required number of character sets '%d' is invalid because it is greater than \
+ 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
diff --git a/opendj-sdk/opends/src/messages/messages/extension_de.properties b/opendj-sdk/opends/src/messages/messages/extension_de.properties
index fc7d154..8d8c7db 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_de.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_de.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=Das angegebene Passwort enthielt zu wenige Zeichen aus dem Zeichensatz '%s'.  Benutzerpassw\u00f6rter m\u00fcssen mindestens %d Zeichen aus diesem Zeichensatz enthalten
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=Die angegebene Zeichensatzdefinition '%s' ist ung\u00fcltig, weil sie keinen Doppelpunkt zur Trennung von Mindestanzahl und Zeichensatz enth\u00e4lt
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=Die angegebene Zeichensatzdefinition '%s' ist ung\u00fcltig, weil der angegebene Zeichensatz leer ist
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=Die angegebene Zeichensatzdefinition '%s' ist ung\u00fcltig, weil der Wert vor dem Doppelpunkt eine Ganzzahl gr\u00f6\u00dfer 0 sein muss
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=Die angegebene Zeichensatzdefinition '%s' ist ung\u00fcltig, weil sie das Zeichen '%s' enth\u00e4lt, das bereits verwendet wurde
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=Die im Eintrag %s definierte virtuelle statische Gruppe enth\u00e4lt mehrere Zielgruppen-DNs, es ist jedoch nur einer zul\u00e4ssig
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471= "%s" kann nicht als Ziel-DN f\u00fcr die Gruppe %s entschl\u00fcsselt werden:  %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_es.properties b/opendj-sdk/opends/src/messages/messages/extension_es.properties
index 616906e..dd66651 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_es.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_es.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=La contrase\u00f1a proporcionada no conten\u00eda suficientes caracteres del conjunto de caracteres '%s'.  El n\u00famero m\u00ednimo de caracteres de dicho conjunto que debe estar presente en las contrase\u00f1as de usuario es %d
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=La definici\u00f3n del conjunto de caracteres proporcionada '%s' no es v\u00e1lida porque no contiene un signo de dos puntos que separe el n\u00famero m\u00ednimo del conjunto de caracteres
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=La definici\u00f3n del conjunto de caracteres proporcionada '%s' no es v\u00e1lida porque el conjunto de caracteres proporcionado est\u00e1 vac\u00edo
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=La definici\u00f3n del conjunto de caracteres proporcionada '%s' no es v\u00e1lida porque el valor que precede al signo de dos puntos debe ser un n\u00famero entero mayor que cero
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=La definici\u00f3n del conjunto de caracteres proporcionada '%s' no es v\u00e1lida porque contiene el car\u00e1cter '%s', el cual ya se ha utilizado
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=El grupo est\u00e1tico virtual definido en la entrada %s contiene varios ND de grupo de destino, pero s\u00f3lo se permite uno
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471=No se puede descodificar "%s" como ND de destino para el grupo %s:  %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_fr.properties b/opendj-sdk/opends/src/messages/messages/extension_fr.properties
index 5dcb94e..bde696a 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_fr.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_fr.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=Le mot de passe fourni ne contient pas suffisamment de caract\u00e8res du jeu de caract\u00e8res '%s'.  Le nombre minimum de caract\u00e8res de ce jeu dans les mots de passe utilisateur doit \u00eatre %d
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=La d\u00e9finition du jeu de caract\u00e8res '%s' fournie n'est pas valide car elle ne contient pas de deux-points pour d\u00e9limiter le nombre minimum du jeu de caract\u00e8res
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=La d\u00e9finition du jeu de caract\u00e8res '%s' fournie n'est pas valide car le jeu fourni est vide
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=La d\u00e9finition du jeu de caract\u00e8res '%s' fournie n'est pas valide car la valeur pr\u00e9c\u00e9dant les deux-points doit \u00eatre un entier sup\u00e9rieur \u00e0 z\u00e9ro
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=La d\u00e9finition du jeu de caract\u00e8res '%s' fournie n'est pas valide car elle contient le caract\u00e8re '%s' qui a d\u00e9j\u00e0 \u00e9t\u00e9 utilis\u00e9
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=Le groupe statique virtuel d\u00e9fini dans l'entr\u00e9e %s contient plusieurs DN de groupe cible, alors qu'un seul est autoris\u00e9
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471=Impossible de d\u00e9coder "%s" comme DN cible pour le groupe %s\u00a0: %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_ja.properties b/opendj-sdk/opends/src/messages/messages/extension_ja.properties
index e411fdc..8530b25 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_ja.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_ja.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=\u6307\u5b9a\u3055\u308c\u305f\u30d1\u30b9\u30ef\u30fc\u30c9\u306b\u3001\u6587\u5b57\u30bb\u30c3\u30c8 '%s' \u306e\u6587\u5b57\u304c\u5341\u5206\u306b\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093\u3067\u3057\u305f\u3002  \u305d\u306e\u6587\u5b57\u30bb\u30c3\u30c8\u304b\u3089\u30e6\u30fc\u30b6\u30fc\u30d1\u30b9\u30ef\u30fc\u30c9\u306b\u542b\u3081\u308b\u5fc5\u8981\u306e\u3042\u308b\u6700\u5c0f\u6587\u5b57\u6570\u306f %d \u3067\u3059
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=\u6307\u5b9a\u3055\u308c\u305f\u6587\u5b57\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9 '%s' \u304c\u7121\u52b9\u3067\u3059\u3002\u6700\u5c0f\u30ab\u30a6\u30f3\u30c8\u3068\u6587\u5b57\u30bb\u30c3\u30c8\u3092\u533a\u5207\u308b\u30b3\u30ed\u30f3\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=\u6307\u5b9a\u3055\u308c\u305f\u6587\u5b57\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9 '%s' \u304c\u7121\u52b9\u3067\u3059\u3002\u6307\u5b9a\u3055\u308c\u305f\u6587\u5b57\u30bb\u30c3\u30c8\u304c\u7a7a\u3067\u3059
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=\u6307\u5b9a\u3055\u308c\u305f\u6587\u5b57\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9 '%s' \u304c\u7121\u52b9\u3067\u3059\u3002\u30b3\u30ed\u30f3\u306e\u524d\u306e\u5024\u306f 0 \u3088\u308a\u5927\u304d\u3044\u6574\u6570\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=\u6307\u5b9a\u3055\u308c\u305f\u6587\u5b57\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9 '%s' \u304c\u7121\u52b9\u3067\u3059\u3002\u3059\u3067\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u6587\u5b57 '%s' \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=\u30a8\u30f3\u30c8\u30ea %s \u3067\u5b9a\u7fa9\u3055\u308c\u305f\u4eee\u60f3\u30b9\u30bf\u30c6\u30a3\u30c3\u30af\u30b0\u30eb\u30fc\u30d7\u306b\u8907\u6570\u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u30b0\u30eb\u30fc\u30d7 DN \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u304c\u3001\u5b9a\u7fa9\u3067\u304d\u308b\u306e\u306f 1 \u3064\u3060\u3051\u3067\u3059
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471="%s" \u3092\u30b0\u30eb\u30fc\u30d7 %s \u306e\u30bf\u30fc\u30b2\u30c3\u30c8 DN \u3068\u3057\u3066\u5fa9\u53f7\u5316\u3067\u304d\u307e\u305b\u3093:  %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_ko.properties b/opendj-sdk/opends/src/messages/messages/extension_ko.properties
index 1ee6bac..7fbf9b5 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_ko.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_ko.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=\uc81c\uacf5\ub41c \ube44\ubc00\ubc88\ud638\uc5d0 \ubb38\uc790 \uc9d1\ud569 '%s'\uc758 \ubb38\uc790\uac00 \ucda9\ubd84\ud788 \ud3ec\ud568\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.  \uc0ac\uc6a9\uc790 \ube44\ubc00\ubc88\ud638\uc5d0 \uc0ac\uc6a9\ub418\uc5b4\uc57c \ud558\ub294 \ud574\ub2f9 \uc9d1\ud569\uc758 \ucd5c\uc18c \ubb38\uc790 \uc218\ub294 %d\uc785\ub2c8\ub2e4.
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=\uc81c\uacf5\ub41c \ubb38\uc790 \uc9d1\ud569 \uc815\uc758 '%s'\uc740(\ub294) \ubb38\uc790 \uc9d1\ud569\uacfc \ucd5c\uc18c \uac1c\uc218\ub97c \uad6c\ubcc4\ud558\ub294 \ucf5c\ub860\uc744 \ud3ec\ud568\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=\uc81c\uacf5\ub41c \ubb38\uc790 \uc9d1\ud569\uc774 \ube44\uc5b4 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc81c\uacf5\ub41c \ubb38\uc790 \uc9d1\ud569 \uc815\uc758 '%s'\uc740(\ub294) \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=\ucf5c\ub860 \uc55e\uc758 \uac12\uc774 0\ubcf4\ub2e4 \ud070 \uc815\uc218\uc5ec\uc57c \ud558\ubbc0\ub85c \uc81c\uacf5\ub41c \ubb38\uc790 \uc9d1\ud569 \uc815\uc758 '%s'\uc740(\ub294) \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=\uc81c\uacf5\ub41c \ubb38\uc790 \uc9d1\ud569 \uc815\uc758 '%s'\uc740(\ub294) \uc774\ubbf8 \uc0ac\uc6a9\ub41c \ubb38\uc790 '%s'\uc744(\ub97c) \ud3ec\ud568\ud558\uae30 \ub54c\ubb38\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=\ud56d\ubaa9 %s\uc5d0 \uc815\uc758\ub41c \uac00\uc0c1 \uc815\uc801 \uadf8\ub8f9\uc5d0 \uc5ec\ub7ec \ub300\uc0c1 \uadf8\ub8f9 DN\uc774 \ud3ec\ud568\ub418\uc5b4 \uc788\uc9c0\ub9cc \ud558\ub098\ub9cc \ud5c8\uc6a9\ub429\ub2c8\ub2e4.
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471=\u20a9"%s\u20a9"\uc744(\ub97c) \uadf8\ub8f9 %s\uc5d0 \ub300\ud55c \ub300\uc0c1 DN\uc73c\ub85c \ud574\ub3c5\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4: %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_zh_CN.properties b/opendj-sdk/opends/src/messages/messages/extension_zh_CN.properties
index 9bf759a..82cf17c 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_zh_CN.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_zh_CN.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=\u63d0\u4f9b\u7684\u5bc6\u7801\u672a\u5305\u542b\u8db3\u591f\u7684\u5b57\u7b26\u96c6 '%s' \u4e2d\u7684\u5b57\u7b26\u3002\u7528\u6237\u5bc6\u7801\u4e2d\u5fc5\u987b\u5305\u542b\u81f3\u5c11 %d \u4e2a\u8be5\u5b57\u7b26\u96c6\u4e2d\u7684\u5b57\u7b26
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=\u63d0\u4f9b\u7684\u5b57\u7b26\u96c6\u5b9a\u4e49 '%s' \u65e0\u6548\uff0c\u56e0\u4e3a\u5b83\u4e0d\u5305\u542b\u5c06\u6700\u5c0f\u8ba1\u6570\u4e0e\u5b57\u7b26\u96c6\u9694\u5f00\u7684\u5192\u53f7
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=\u63d0\u4f9b\u7684\u5b57\u7b26\u96c6\u5b9a\u4e49 '%s' \u65e0\u6548\uff0c\u56e0\u4e3a\u63d0\u4f9b\u7684\u5b57\u7b26\u96c6\u4e3a\u7a7a
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=\u63d0\u4f9b\u7684\u5b57\u7b26\u96c6\u5b9a\u4e49 '%s' \u65e0\u6548\uff0c\u56e0\u4e3a\u5192\u53f7\u524d\u9762\u7684\u503c\u5fc5\u987b\u4e3a\u5927\u4e8e\u96f6\u7684\u6574\u6570
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=\u63d0\u4f9b\u7684\u5b57\u7b26\u96c6\u5b9a\u4e49 '%s' \u65e0\u6548\uff0c\u56e0\u4e3a\u5b83\u5305\u542b\u5df2\u4f7f\u7528\u7684\u5b57\u7b26 '%s'
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=\u6761\u76ee %s \u4e2d\u5b9a\u4e49\u7684\u865a\u62df\u9759\u6001\u7ec4\u5305\u542b\u591a\u4e2a\u76ee\u6807\u7ec4 DN\uff0c\u4f46\u53ea\u5141\u8bb8\u5305\u542b\u4e00\u4e2a\u76ee\u6807\u7ec4 DN
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471=\u65e0\u6cd5\u5c06 "%s" \u89e3\u7801\u4e3a\u7ec4 %s \u7684\u76ee\u6807 DN: %s
diff --git a/opendj-sdk/opends/src/messages/messages/extension_zh_TW.properties b/opendj-sdk/opends/src/messages/messages/extension_zh_TW.properties
index 65cf763..0365ba1 100644
--- a/opendj-sdk/opends/src/messages/messages/extension_zh_TW.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension_zh_TW.properties
@@ -426,7 +426,6 @@
 MILD_ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET_465=\u63d0\u4f9b\u7684\u5bc6\u78bc\u672a\u5305\u542b\u4f86\u81ea\u5b57\u5143\u96c6\u300c%s\u300d\u7684\u8db3\u5920\u5b57\u5143\u3002\u8a72\u96c6\u5408\u4e2d\u5fc5\u9808\u51fa\u73fe\u65bc\u4f7f\u7528\u8005\u5bc6\u78bc\u4e2d\u7684\u6700\u5c0f\u5b57\u5143\u6578\u70ba %d
 MILD_ERR_CHARSET_VALIDATOR_NO_COLON_466=\u63d0\u4f9b\u7684\u5b57\u5143\u96c6\u5b9a\u7fa9\u300c%s\u300d\u7121\u6548\uff0c\u56e0\u70ba\u5b83\u4e0d\u5305\u542b\u5192\u865f\u4ee5\u9694\u958b\u6700\u5c0f\u8a08\u6578\u8207\u5b57\u5143\u96c6
 MILD_ERR_CHARSET_VALIDATOR_NO_CHARS_467=\u63d0\u4f9b\u7684\u5b57\u5143\u96c6\u5b9a\u7fa9\u300c%s\u300d\u7121\u6548\uff0c\u56e0\u70ba\u63d0\u4f9b\u7684\u5b57\u5143\u96c6\u662f\u7a7a\u7684
-MILD_ERR_CHARSET_VALIDATOR_INVALID_COUNT_468=\u63d0\u4f9b\u7684\u5b57\u5143\u96c6\u5b9a\u7fa9\u300c%s\u300d\u7121\u6548\uff0c\u56e0\u70ba\u5192\u865f\u524d\u9762\u7684\u503c\u5fc5\u9808\u662f\u5927\u65bc\u96f6\u7684\u6574\u6578
 MILD_ERR_CHARSET_VALIDATOR_DUPLICATE_CHAR_469=\u63d0\u4f9b\u7684\u5b57\u5143\u96c6\u5b9a\u7fa9\u300c%s\u300d\u7121\u6548\uff0c\u56e0\u70ba\u5b83\u5305\u542b\u5df2\u4f7f\u7528\u7684\u5b57\u5143\u300c%s\u300d
 MILD_ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS_470=\u9805\u76ee %s \u4e2d\u5b9a\u7fa9\u7684\u865b\u64ec\u975c\u614b\u7fa4\u7d44\u5305\u542b\u591a\u500b\u76ee\u6a19\u7fa4\u7d44 DN\uff0c\u4f46\u53ea\u5141\u8a31\u4e00\u500b
 MILD_ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET_471=\u7121\u6cd5\u5c07\u300c%s\u300d\u89e3\u78bc\u70ba\u7fa4\u7d44 %s \u7684\u76ee\u6a19 DN: %s
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
index 96fc7de..92ed18d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.extensions;
 import org.opends.messages.Message;
@@ -41,7 +42,6 @@
 import org.opends.server.api.PasswordValidator;
 import org.opends.server.config.ConfigException;
 import org.opends.server.types.*;
-
 import static org.opends.messages.ExtensionMessages.*;
 import org.opends.messages.MessageBuilder;
 import static org.opends.server.util.StaticUtils.*;
@@ -155,18 +155,74 @@
       }
     }
 
+    int usedOptionalCharacterSets = 0;
+    int optionalCharacterSets = 0;
+    int mandatoryCharacterSets = 0;
     for (String characterSet : characterSets.keySet())
     {
       int minimumCount = characterSets.get(characterSet);
       Integer passwordCount = counts.get(characterSet);
-      if ((passwordCount == null) || (passwordCount < minimumCount))
+      if (minimumCount > 0)
       {
-        invalidReason.append(ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET.get(
-                characterSet, minimumCount));
-        return false;
+        // Mandatory character set.
+        mandatoryCharacterSets++;
+        if ((passwordCount == null) || (passwordCount < minimumCount))
+        {
+          invalidReason
+              .append(ERR_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET
+                  .get(characterSet, minimumCount));
+          return false;
+        }
+      }
+      else
+      {
+        // Optional character set.
+        optionalCharacterSets++;
+        if (passwordCount != null)
+        {
+          usedOptionalCharacterSets++;
+        }
       }
     }
 
+    // Check minimum optional character sets are present.
+    if (optionalCharacterSets > 0)
+    {
+      int requiredOptionalCharacterSets;
+      if (currentConfig.getMinCharacterSets() == null)
+      {
+        requiredOptionalCharacterSets = 1;
+      }
+      else
+      {
+        requiredOptionalCharacterSets = currentConfig
+            .getMinCharacterSets() - mandatoryCharacterSets;
+      }
+
+      if (usedOptionalCharacterSets < requiredOptionalCharacterSets)
+      {
+        StringBuilder builder = new StringBuilder();
+        for (String characterSet : characterSets.keySet())
+        {
+          if (characterSets.get(characterSet) == 0)
+          {
+            if (builder.length() > 0)
+            {
+              builder.append(", ");
+            }
+            builder.append('\'');
+            builder.append(characterSet);
+            builder.append('\'');
+          }
+        }
+
+        invalidReason
+            .append(ERR_CHARSET_VALIDATOR_TOO_FEW_OPTIONAL_CHAR_SETS
+                .get(requiredOptionalCharacterSets,
+                    builder.toString()));
+        return false;
+      }
+    }
 
     // If we've gotten here, then the password is acceptable.
     return true;
@@ -194,6 +250,7 @@
   {
     HashMap<String,Integer> characterSets  = new HashMap<String,Integer>();
     HashSet<Character>      usedCharacters = new HashSet<Character>();
+    int mandatoryCharacterSets = 0;
 
     for (String definition : configuration.getCharacterSet())
     {
@@ -220,7 +277,7 @@
         throw new ConfigException(message);
       }
 
-      if (minCount <= 0)
+      if (minCount < 0)
       {
         Message message = ERR_CHARSET_VALIDATOR_INVALID_COUNT.get(definition);
         throw new ConfigException(message);
@@ -241,6 +298,34 @@
       }
 
       characterSets.put(characterSet, minCount);
+
+      if (minCount > 0)
+      {
+        mandatoryCharacterSets++;
+      }
+    }
+
+    // Validate min-character-sets if necessary.
+    int optionalCharacterSets = characterSets.size()
+        - mandatoryCharacterSets;
+    if (optionalCharacterSets > 0
+        && configuration.getMinCharacterSets() != null)
+    {
+      int minCharacterSets = configuration.getMinCharacterSets();
+
+      if (minCharacterSets <= mandatoryCharacterSets)
+      {
+        Message message = ERR_CHARSET_VALIDATOR_MIN_CHAR_SETS_TOO_SMALL
+            .get(minCharacterSets);
+        throw new ConfigException(message);
+      }
+
+      if (minCharacterSets > characterSets.size())
+      {
+        Message message = ERR_CHARSET_VALIDATOR_MIN_CHAR_SETS_TOO_BIG
+            .get(minCharacterSets);
+        throw new ConfigException(message);
+      }
     }
 
     return characterSets;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
index 7e59728..e99a3d5 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -125,7 +126,20 @@
          "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
          "ds-cfg-character-set: 1:0123456789",
          "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
-         "ds-cfg-allow-unclassified-characters: false");
+         "ds-cfg-allow-unclassified-characters: false",
+         "",
+         "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: 1:abcdefghijklmnopqrstuvwxyz",
+         "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];
     for (int i=0; i < array.length; i++)
@@ -219,18 +233,6 @@
          "ds-cfg-character-set: noninteger:abcdefghijklmnopqrstuvwxyz",
          "ds-cfg-allow-unclassified-characters: true",
          "",
-         // Malformed character set definition -- zero 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: 0:abcdefghijklmnopqrstuvwxyz",
-         "ds-cfg-allow-unclassified-characters: true",
-         "",
          // Malformed character set definition -- negative count.
          "dn: cn=Character Set,cn=Password Validators,cn=config",
          "objectClass: top",
@@ -280,7 +282,49 @@
               "CharacterSetPasswordValidator",
          "ds-cfg-enabled: true",
          "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
-         "ds-cfg-allow-unclassified-characters: malformed");
+         "ds-cfg-allow-unclassified-characters: malformed",
+         "",
+         // Malformed min-character-sets: too low.
+         "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: 1:abcdefghijklmnopqrstuvwxyz",
+         "ds-cfg-character-set: 0:0123456789",
+         "ds-cfg-allow-unclassified-characters: true",
+         "ds-cfg-min-character-sets: 0",
+         "",
+         // Malformed min-character-sets: too low.
+         "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: 1:abcdefghijklmnopqrstuvwxyz",
+         "ds-cfg-character-set: 0:0123456789",
+         "ds-cfg-allow-unclassified-characters: true",
+         "ds-cfg-min-character-sets: 1",
+         "",
+         // Malformed min-character-sets: too high.
+         "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: 1:abcdefghijklmnopqrstuvwxyz",
+         "ds-cfg-character-set: 0:0123456789",
+         "ds-cfg-allow-unclassified-characters: true",
+         "ds-cfg-min-character-sets: 3");
 
     Object[][] array = new Object[entries.size()][1];
     for (int i=0; i < array.length; i++)
@@ -479,6 +523,174 @@
         "PaS$w0rD",
         false
       },
+
+      // 1 mandatory, 2 optional, must have at least one optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 1:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "!@#$%",
+        false
+      },
+
+      // 1 mandatory, 2 optional, must have at least one optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 1:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "0123456",
+        false
+      },
+
+      // 1 mandatory, 2 optional, must have at least one optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 1:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abcDEF",
+        false
+      },
+
+      // 1 mandatory, 2 optional, must have at least one optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 1:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abc123",
+        true
+      },
+
+      // 1 mandatory, 2 optional, must have at least one optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 1:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abc123ABC",
+        true
+      },
+
+      // 3 optional, must have at least two optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 0:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abcdef",
+        false
+      },
+
+      // 3 optional, must have at least two optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 0:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abc123",
+        true
+      },
+
+      // 3 optional, must have at least two optional.
+      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: 0:abcdefghijklmnopqrstuvwxyz",
+             "ds-cfg-character-set: 0:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+             "ds-cfg-character-set: 0:0123456789",
+             "ds-cfg-min-character-sets: 2",
+             "ds-cfg-allow-unclassified-characters: false"),
+        "abc123ABC",
+        true
+      },
     };
   }
 

--
Gitblit v1.10.0