From 20a5a72f2842bddd089de5d161b3e2b1c644a3d4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 26 Mar 2007 22:00:15 +0000
Subject: [PATCH] Add two new password validators to OpenDS:
---
opends/resource/schema/02-config.ldif | 22
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidatorTestCase.java | 699 ++++++++++++++++++++
opends/src/admin/defn/org/opends/server/admin/std/UniqueCharactersPasswordValidatorConfiguration.xml | 64 +
opends/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidator.java | 187 +++++
opends/src/admin/defn/org/opends/server/admin/std/RepeatedCharactersPasswordValidatorConfiguration.xml | 66 +
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidatorTestCase.java | 699 ++++++++++++++++++++
opends/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidator.java | 196 +++++
opends/resource/config/config.ldif | 20
opends/src/server/org/opends/server/messages/ExtensionsMessages.java | 32
9 files changed, 1,985 insertions(+), 0 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 4679b65..6c192c1 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -1098,6 +1098,16 @@
ds-cfg-minimum-password-length: 6
ds-cfg-maximum-password-length: 0
+dn: cn=Repeated Characters,cn=Password Validators,cn=config
+objectClass: top
+objectClass: ds-cfg-password-validator
+objectClass: ds-cfg-repeated-characters-password-validator
+cn: Repeated Characters
+ds-cfg-password-validator-class: org.opends.server.extensions.RepeatedCharactersPasswordValidator
+ds-cfg-password-validator-enabled: true
+ds-cfg-maximum-consecutive-length: 2
+ds-cfg-case-sensitive-validation: false
+
dn: cn=Similarity-Based Password Validator,cn=Password Validators,cn=config
objectClass: top
objectClass: ds-cfg-password-validator
@@ -1107,6 +1117,16 @@
ds-cfg-password-validator-enabled: true
ds-cfg-minimum-password-difference: 3
+dn: cn=Unique Characters,cn=Password Validators,cn=config
+objectClass: top
+objectClass: ds-cfg-password-validator
+objectClass: ds-cfg-unique-characters-password-validator
+cn: Unique Characters
+ds-cfg-password-validator-class: org.opends.server.extensions.UniqueCharactersPasswordValidator
+ds-cfg-password-validator-enabled: true
+ds-cfg-minimum-unique-characters: 5
+ds-cfg-case-sensitive-validation: false
+
dn: cn=Plugins,cn=config
objectClass: top
objectClass: ds-cfg-branch
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index c6e712e..b5791ca 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -1091,6 +1091,18 @@
NAME 'ds-cfg-minimum-password-difference'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.322
+ NAME 'ds-cfg-minimum-unique-characters'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.323
+ NAME 'ds-cfg-maximum-consecutive-length'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.324
+ NAME 'ds-cfg-case-sensitive-validation'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1510,4 +1522,14 @@
NAME 'ds-cfg-similarity-based-password-validator'
SUP ds-cfg-password-validator STRUCTURAL
MUST ds-cfg-minimum-password-difference X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.89
+ NAME 'ds-cfg-unique-characters-password-validator'
+ SUP ds-cfg-password-validator STRUCTURAL
+ MUST ( ds-cfg-minimum-unique-characters $ ds-cfg-case-sensitive-validation )
+ X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.90
+ NAME 'ds-cfg-repeated-characters-password-validator'
+ SUP ds-cfg-password-validator STRUCTURAL
+ MUST ( ds-cfg-maximum-consecutive-length $ ds-cfg-case-sensitive-validation )
+ X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RepeatedCharactersPasswordValidatorConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RepeatedCharactersPasswordValidatorConfiguration.xml
new file mode 100644
index 0000000..86c3fb9
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RepeatedCharactersPasswordValidatorConfiguration.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<adm:managed-object name="repeated-characters-password-validator"
+plural-name="repeated-characters-password-validators"
+package="org.opends.server.admin.std" extends="password-validator"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ is used to determine whether a proposed password is acceptable based on
+ the number of times any character may appear consecutively in a password
+ value.
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>1.3.6.1.4.1.26027.1.2.90</ldap:oid>
+ <ldap:name>ds-cfg-repeated-characters-password-validator</ldap:name>
+ <ldap:superior>ds-cfg-password-validator</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property name="maximum-consecutive-length" mandatory="true">
+ <adm:synopsis>
+ Specifies the maximum number of times that any character may appear
+ consecutively in a password value.
+ </adm:synopsis>
+ <adm:description>
+ Specifies the maximum number of times that any character may appear
+ consecutively in a password value. A value of zero indicates that there
+ will be no maximum limit enforced. Changes to this configuration
+ attribute will take effect immediately.
+ </adm:description>
+ <adm:syntax>
+ <adm:integer lower-limit="0" />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.323</ldap:oid>
+ <ldap:name>ds-cfg-maximum-consecutive-length</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="case-sensitive-validation" mandatory="true">
+ <adm:synopsis>
+ Indicates whether this password validator should treat password characters
+ in a case-sensitive manner.
+ </adm:synopsis>
+ <adm:description>
+ Indicates whether this password validator should treat password characters
+ in a case-sensitive manner. A value of false indicates that any
+ differences in capitalization should be ignored when looking for
+ consecutive characters in the password. A value of true indicates that
+ a character should only be considered repeating if all consecutive
+ occurrences use the same capitalization.
+ </adm:description>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.324</ldap:oid>
+ <ldap:name>ds-cfg-case-sensitive-validation</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+</adm:managed-object>
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/UniqueCharactersPasswordValidatorConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/UniqueCharactersPasswordValidatorConfiguration.xml
new file mode 100644
index 0000000..18b0586
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/UniqueCharactersPasswordValidatorConfiguration.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<adm:managed-object name="unique-characters-password-validator"
+plural-name="unique-characters-password-validators"
+package="org.opends.server.admin.std" extends="password-validator"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ is used to determine whether a proposed password is acceptable based on
+ the number of unique characters that it contains.
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>1.3.6.1.4.1.26027.1.2.89</ldap:oid>
+ <ldap:name>ds-cfg-unique-characters-password-validator</ldap:name>
+ <ldap:superior>ds-cfg-password-validator</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property name="minimum-unique-characters" mandatory="true">
+ <adm:synopsis>
+ Specifies the minimum number of unique characters that a password will be
+ allowed to contain.
+ </adm:synopsis>
+ <adm:description>
+ Specifies the minimum number of unique characters that a password will be
+ allowed to contain. A value of zero indicates that there will be no
+ minimum value enforced. Changes to this configuration attribute will take
+ effect immediately.
+ </adm:description>
+ <adm:syntax>
+ <adm:integer lower-limit="0" />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.322</ldap:oid>
+ <ldap:name>ds-cfg-minimum-unique-characters</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="case-sensitive-validation" mandatory="true">
+ <adm:synopsis>
+ Indicates whether this password validator should treat password characters
+ in a case-sensitive manner.
+ </adm:synopsis>
+ <adm:description>
+ Indicates whether this password validator should treat password characters
+ in a case-sensitive manner. A value of true indicates that a capital
+ letter should not be considered the same as its lowercase counterpart.
+ A value of false indicates that differences in capitalization should be
+ ignored when looking at the number of unique characters in the password.
+ </adm:description>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.324</ldap:oid>
+ <ldap:name>ds-cfg-case-sensitive-validation</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+</adm:managed-object>
+
diff --git a/opends/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidator.java b/opends/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidator.java
new file mode 100644
index 0000000..0811659
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidator.java
@@ -0,0 +1,196 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.
+ RepeatedCharactersPasswordValidatorCfg;
+import org.opends.server.api.PasswordValidator;
+import org.opends.server.core.Operation;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+
+
+/**
+ * This class provides an OpenDS password validator that may be used to ensure
+ * that proposed passwords are not allowed to have the same character appear
+ * several times consecutively.
+ */
+public class RepeatedCharactersPasswordValidator
+ extends PasswordValidator<RepeatedCharactersPasswordValidatorCfg>
+ implements ConfigurationChangeListener<
+ RepeatedCharactersPasswordValidatorCfg>
+{
+ // The current configuration for this password validator.
+ private RepeatedCharactersPasswordValidatorCfg currentConfig;
+
+
+
+ /**
+ * Creates a new instance of this repeated characters password validator.
+ */
+ public RepeatedCharactersPasswordValidator()
+ {
+ super();
+
+ // No implementation is required here. All initialization should be
+ // performed in the initializePasswordValidator() method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializePasswordValidator(
+ RepeatedCharactersPasswordValidatorCfg configuration)
+ {
+ configuration.addRepeatedCharactersChangeListener(this);
+ currentConfig = configuration;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void finalizePasswordValidator()
+ {
+ currentConfig.removeRepeatedCharactersChangeListener(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
+ Operation operation, Entry userEntry,
+ StringBuilder invalidReason)
+ {
+ // Get a handle to the current configuration and see if we need to count
+ // the number of repeated characters in the password.
+ RepeatedCharactersPasswordValidatorCfg config = currentConfig;
+ int maxRepeats = config.getMaximumConsecutiveLength();
+ if (maxRepeats <= 0)
+ {
+ // We don't need to check anything, so the password will be acceptable.
+ return true;
+ }
+
+
+ // Get the password as a string. If we should use case-insensitive
+ // validation, then convert it to use all lowercase characters.
+ String passwordString = newPassword.stringValue();
+ if (! config.isCaseSensitiveValidation())
+ {
+ passwordString = passwordString.toLowerCase();
+ }
+
+
+ // Create variables to keep track of the last character we've seen and how
+ // many times we have seen it.
+ char lastCharacter = '\u0000';
+ int consecutiveCount = 0;
+
+
+ // Iterate through the characters in the password. If the consecutive
+ // count ever gets too high, then fail.
+ for (int i=0; i < passwordString.length(); i++)
+ {
+ char currentCharacter = passwordString.charAt(i);
+ if (currentCharacter == lastCharacter)
+ {
+ consecutiveCount++;
+ if (consecutiveCount > maxRepeats)
+ {
+ int msgID = MSGID_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE;
+ String message = getMessage(msgID, maxRepeats);
+ invalidReason.append(message);
+ return false;
+ }
+ }
+ else
+ {
+ lastCharacter = currentCharacter;
+ consecutiveCount = 1;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(
+ RepeatedCharactersPasswordValidatorCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ // All of the necessary validation should have been performed automatically,
+ // so if we get to this point then the new configuration will be acceptable.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(
+ RepeatedCharactersPasswordValidatorCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+ // For this password validator, we will always be able to successfully apply
+ // the new configuration.
+ currentConfig = configuration;
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opends/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidator.java b/opends/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidator.java
new file mode 100644
index 0000000..a734c6c
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidator.java
@@ -0,0 +1,187 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.UniqueCharactersPasswordValidatorCfg;
+import org.opends.server.api.PasswordValidator;
+import org.opends.server.core.Operation;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+
+
+/**
+ * This class provides an OpenDS password validator that may be used to ensure
+ * that proposed passwords contain at least a specified number of different
+ * characters.
+ */
+public class UniqueCharactersPasswordValidator
+ extends PasswordValidator<UniqueCharactersPasswordValidatorCfg>
+ implements ConfigurationChangeListener<
+ UniqueCharactersPasswordValidatorCfg>
+{
+ // The current configuration for this password validator.
+ private UniqueCharactersPasswordValidatorCfg currentConfig;
+
+
+
+ /**
+ * Creates a new instance of this unique characters password validator.
+ */
+ public UniqueCharactersPasswordValidator()
+ {
+ super();
+
+ // No implementation is required here. All initialization should be
+ // performed in the initializePasswordValidator() method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializePasswordValidator(
+ UniqueCharactersPasswordValidatorCfg configuration)
+ {
+ configuration.addUniqueCharactersChangeListener(this);
+ currentConfig = configuration;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void finalizePasswordValidator()
+ {
+ currentConfig.removeUniqueCharactersChangeListener(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
+ Operation operation, Entry userEntry,
+ StringBuilder invalidReason)
+ {
+ // Get a handle to the current configuration and see if we need to count
+ // the number of unique characters in the password.
+ UniqueCharactersPasswordValidatorCfg config = currentConfig;
+ int minUniqueCharacters = config.getMinimumUniqueCharacters();
+ if (minUniqueCharacters <= 0)
+ {
+ // We don't need to check anything, so the password will be acceptable.
+ return true;
+ }
+
+
+
+ // Create a set that will be used to keep track of the unique characters
+ // contained in the proposed password.
+ HashSet<Character> passwordCharacters = new HashSet<Character>();
+
+ // Iterate through the characters in the new password and place them in the
+ // set as needed. If we should behave in a case-insensitive manner, then
+ // convert all the characters to lowercase first.
+ String passwordString = newPassword.stringValue();
+ if (! config.isCaseSensitiveValidation())
+ {
+ passwordString = passwordString.toLowerCase();
+ }
+
+ for (int i=0; i < passwordString.length(); i++)
+ {
+ passwordCharacters.add(passwordString.charAt(i));
+ }
+
+ // If the size of the password characters set is less than the minimum
+ // number of allowed unique characters, then we will reject the password.
+ if (passwordCharacters.size() < minUniqueCharacters)
+ {
+ int msgID = MSGID_UNIQUECHARS_VALIDATOR_NOT_ENOUGH_UNIQUE_CHARS;
+ String message = getMessage(msgID, minUniqueCharacters);
+ invalidReason.append(message);
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(
+ UniqueCharactersPasswordValidatorCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ // All of the necessary validation should have been performed automatically,
+ // so if we get to this point then the new configuration will be acceptable.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(
+ UniqueCharactersPasswordValidatorCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+ // For this password validator, we will always be able to successfully apply
+ // the new configuration.
+ currentConfig = configuration;
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index ae4670c..bc856f8 100644
--- a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4807,6 +4807,25 @@
/**
+ * The message ID for the message that will be used if the same character
+ * appears too many times in consecutive order in a given password. This
+ * takes a single argument, which is the maximum number of times a character
+ * may appear in consecutive order.
+ */
+ public static final int MSGID_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 457;
+
+
+ /**
+ * The message ID for the message that will be used if a given password does
+ * not have enough unique characters. This takes a single argument, which is
+ * the minimum number of unique characters that a password may contain.
+ */
+ public static final int MSGID_UNIQUECHARS_VALIDATOR_NOT_ENOUGH_UNIQUE_CHARS =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 458;
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -6930,6 +6949,19 @@
registerMessage(MSGID_DYNAMICGROUP_CANNOT_RETURN_ENTRY,
"The server encountered a timeout while attempting to " +
"add user %s to the member list for dynamic group %s.");
+
+
+ registerMessage(MSGID_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE,
+ "The provided password contained too many instances " +
+ "of the same character appearing consecutively. The " +
+ "maximum number of times the same character may appear " +
+ "consecutively in a password is %d.");
+
+
+ registerMessage(MSGID_UNIQUECHARS_VALIDATOR_NOT_ENOUGH_UNIQUE_CHARS,
+ "The provided password does not contain enough unique " +
+ "characters. The minimum number of unique characters " +
+ "that may appear in a user password is %d.");
}
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidatorTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidatorTestCase.java
new file mode 100644
index 0000000..c0672b7
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidatorTestCase.java
@@ -0,0 +1,699 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.
+ RepeatedCharactersPasswordValidatorCfgDefn;
+import org.opends.server.admin.std.server.
+ RepeatedCharactersPasswordValidatorCfg;
+import org.opends.server.admin.server.AdminTestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the repeated characters password validator.
+ */
+public class RepeatedCharactersPasswordValidatorTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of valid configuration entries that may be used to
+ * initialize the validator.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "validConfigs")
+ public Object[][] getValidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: true",
+ "",
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 0",
+ "ds-cfg-case-sensitive-validation: false");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with valid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "validConfigs")
+ public void testInitializeWithValidConfigs(Entry e)
+ throws Exception
+ {
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(), e);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configuration entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ // Missing maximum consecutive length
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ // Missing case-sensitive validation
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "",
+ // Non-numeric maximum consecutive length
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: non-numeric",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ // Non-boolean case-sensitive validation
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: non-boolean",
+ "",
+ // Maximum consecutive length out of range.
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: -1",
+ "ds-cfg-case-sensitive-validation: false");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with invalid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInitializeWithInvalidConfigs(Entry e)
+ throws Exception
+ {
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(), e);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * within the constraints of the password validator. Case-sensitivity will
+ * not be an issue.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptable2Consecutive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: false");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("password");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "password")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * outside of the constraints of the password validator. Case-sensitivity
+ * will not be an issue.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptable3Consecutive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: false");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("passsword");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "passsword")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * within the constraints of the password validator only because it is
+ * configured to operate in a case-sensitive manner.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableCaseSensitive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: true");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("passSword");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "passSword")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * outside of the constraints of the password validator because it is
+ * configured to operate in a case-insensitive manner.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableCaseInsensitive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: false");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("passSword");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "passSword")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method when the validator is
+ * configured to accept any number of repeated characters.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableUnlimitedRepeats()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 0",
+ "ds-cfg-case-sensitive-validation: true");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("aaaaaaaa");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "aaaaaaaa")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the ability of the password validator to change its behavior when
+ * the configuration is updated.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableConfigurationChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 0",
+ "ds-cfg-case-sensitive-validation: true");
+
+ RepeatedCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ RepeatedCharactersPasswordValidator validator =
+ new RepeatedCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("aaaaaaaa");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "aaaaaaaa")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ Entry updatedValidatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Repeated Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-repeated-characters-password-validator",
+ "cn: Repeated Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "RepeatedCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-maximum-consecutive-length: 2",
+ "ds-cfg-case-sensitive-validation: true");
+
+ RepeatedCharactersPasswordValidatorCfg updatedConfiguration =
+ AdminTestCaseUtils.getConfiguration(
+ RepeatedCharactersPasswordValidatorCfgDefn.getInstance(),
+ updatedValidatorEntry);
+
+ ArrayList<String> unacceptableReasons = new ArrayList<String>();
+ assertTrue(validator.isConfigurationChangeAcceptable(updatedConfiguration,
+ unacceptableReasons),
+ String.valueOf(unacceptableReasons));
+
+ ConfigChangeResult changeResult =
+ validator.applyConfigurationChange(updatedConfiguration);
+ assertEquals(changeResult.getResultCode(), ResultCode.SUCCESS);
+
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidatorTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidatorTestCase.java
new file mode 100644
index 0000000..d5e4fe0
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidatorTestCase.java
@@ -0,0 +1,699 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.
+ UniqueCharactersPasswordValidatorCfgDefn;
+import org.opends.server.admin.std.server.
+ UniqueCharactersPasswordValidatorCfg;
+import org.opends.server.admin.server.AdminTestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the unique characters password validator.
+ */
+public class UniqueCharactersPasswordValidatorTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of valid configuration entries that may be used to
+ * initialize the validator.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "validConfigs")
+ public Object[][] getValidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: true",
+ "",
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 0",
+ "ds-cfg-case-sensitive-validation: false");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with valid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "validConfigs")
+ public void testInitializeWithValidConfigs(Entry e)
+ throws Exception
+ {
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(), e);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configuration entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ // Missing minimum unique characters
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ // Missing case-sensitive validation
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "",
+ // Non-numeric minimum unique characters
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: non-numeric",
+ "ds-cfg-case-sensitive-validation: false",
+ "",
+ // Non-boolean case-sensitive validation
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: non-boolean",
+ "",
+ // Minimum unique characters out of range.
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: -1",
+ "ds-cfg-case-sensitive-validation: false");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with invalid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInitializeWithInvalidConfigs(Entry e)
+ throws Exception
+ {
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(), e);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * within the constraints of the password validator. Case-sensitivity will
+ * not be an issue.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptable7Unique()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: false");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("password");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "password")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * outside the constraints of the password validator. Case-sensitivity will
+ * not be an issue.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptable4Unique()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: false");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("passw");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "passw")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * within the constraints of the password validator only because it uses
+ * case-sensitive validation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableCaseSensitive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: true");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("pasSw");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "pasSw")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method with a password that falls
+ * outside the constraints of the password validator because it uses
+ * case-insensitive validation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableCaseInsensitive()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: false");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("pasSw");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "pasSw")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method when the validator is
+ * configured to accept any number of unique characters.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableAnyNumberOfCharacters()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 0",
+ "ds-cfg-case-sensitive-validation: true");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("aaaaaaaa");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "aaaaaaaa")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Tests the ability of the password validator to change its behavior when
+ * the configuration is updated.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testPasswordIsAcceptableConfigurationChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ Entry validatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 0",
+ "ds-cfg-case-sensitive-validation: true");
+
+ UniqueCharactersPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ validatorEntry);
+
+ UniqueCharactersPasswordValidator validator =
+ new UniqueCharactersPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString password = new ASN1OctetString("aaaaaaaa");
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", "aaaaaaaa")));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ invalidReason.toString());
+
+ Entry updatedValidatorEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Unique Characters,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-unique-characters-password-validator",
+ "cn: Unique Characters",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "UniqueCharactersPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-minimum-unique-characters: 5",
+ "ds-cfg-case-sensitive-validation: true");
+
+ UniqueCharactersPasswordValidatorCfg updatedConfiguration =
+ AdminTestCaseUtils.getConfiguration(
+ UniqueCharactersPasswordValidatorCfgDefn.getInstance(),
+ updatedValidatorEntry);
+
+ ArrayList<String> unacceptableReasons = new ArrayList<String>();
+ assertTrue(validator.isConfigurationChangeAcceptable(updatedConfiguration,
+ unacceptableReasons),
+ String.valueOf(unacceptableReasons));
+
+ ConfigChangeResult changeResult =
+ validator.applyConfigurationChange(updatedConfiguration);
+ assertEquals(changeResult.getResultCode(), ResultCode.SUCCESS);
+
+ assertFalse(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason));
+
+ validator.finalizePasswordValidator();
+ }
+}
+
--
Gitblit v1.10.0