From 922296e6b25dc68bbecf1f415ba3a61aa0105b99 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Fri, 06 Oct 2006 17:01:56 +0000
Subject: [PATCH] Update the password validator API in the following ways:
---
opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java | 22
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java | 4
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/PasswordValidatorTestCase.java | 866 ++++++++++++++++++++++++++++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java | 43 +
opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java | 22
opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif | 16
opendj-sdk/opends/src/server/org/opends/server/extensions/LengthBasedPasswordValidator.java | 87 --
opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java | 45 +
opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java | 6
opendj-sdk/opends/src/server/org/opends/server/api/PasswordValidator.java | 33
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/TestPasswordValidator.java | 237 +++++++++
11 files changed, 1,270 insertions(+), 111 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/PasswordValidator.java b/opendj-sdk/opends/src/server/org/opends/server/api/PasswordValidator.java
index d9cdfbf..195a2df 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/PasswordValidator.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/PasswordValidator.java
@@ -28,6 +28,8 @@
+import java.util.Set;
+
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.Operation;
@@ -98,22 +100,29 @@
* unacceptable, then a human-readable explanation should be
* appended to the provided buffer.
*
- * @param password The proposed clear-text password that
- * should be validated.
- * @param operation The operation that is being used to set
- * the password. It may be an add, a
- * modify, or a password modify operation.
- * @param userEntry The entry for the user whose password is
- * being changed.
- * @param invalidReason The buffer to which the human-readable
- * explanation should be appended if it is
- * determined that the password is not
- * acceptable.
+ * @param newPassword The proposed clear-text password that
+ * should be validated.
+ * @param currentPasswords The set of clear-text current passwords
+ * for the user (if available). Note that
+ * the current passwords may not always be
+ * available, and this may not comprise
+ * entire set of passwords currently
+ * for the user.
+ * @param operation The operation that is being used to set
+ * the password. It may be an add, a
+ * modify, or a password modify operation.
+ * @param userEntry The entry for the user whose password
+ * is being changed.
+ * @param invalidReason The buffer to which the human-readable
+ * explanation should be appended if it is
+ * determined that the password is not
+ * acceptable.
*
* @return <CODE>true</CODE> if the password is acceptable, or
* <CODE>false</CODE> if not.
*/
- public abstract boolean passwordIsValid(ByteString password,
+ public abstract boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
Operation operation,
Entry userEntry,
StringBuilder invalidReason);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
index 26510ee..249dfe9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
@@ -2298,12 +2298,14 @@
// validation should be performed for administrators.
if (! passwordPolicy.skipValidationForAdministrators())
{
+ // There are never any current passwords for an add operation.
+ HashSet<ByteString> currentPasswords = new HashSet<ByteString>(0);
StringBuilder invalidReason = new StringBuilder();
for (PasswordValidator validator :
passwordPolicy.getPasswordValidators().values())
{
- if (! validator.passwordIsValid(value, this, userEntry,
- invalidReason))
+ if (! validator.passwordIsAcceptable(value, currentPasswords, this,
+ userEntry, invalidReason))
{
int msgID = MSGID_PWPOLICY_VALIDATION_FAILED;
String message = getMessage(msgID, passwordAttribute.getNameOrOID(),
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
index 79f8743..5cb7369 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -29,6 +29,8 @@
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -2190,11 +2192,54 @@
{
if (newPasswords != null)
{
+ HashSet<ByteString> clearPasswords = new HashSet<ByteString>();
+ clearPasswords.addAll(pwPolicyState.getClearPasswords());
+
+ if (currentPasswords != null)
+ {
+ if (clearPasswords.isEmpty())
+ {
+ for (AttributeValue v : currentPasswords)
+ {
+ clearPasswords.add(v.getValue());
+ }
+ }
+ else
+ {
+ // NOTE: We can't rely on the fact that Set doesn't allow
+ // duplicates because technically it's possible that the
+ // values aren't duplicates if they are ASN.1 elements with
+ // different types (like 0x04 for a standard universal octet
+ // string type versus 0x80 for a simple password in a bind
+ // operation). So we have to manually check for duplicates.
+ for (AttributeValue v : currentPasswords)
+ {
+ ByteString pw = v.getValue();
+
+ boolean found = false;
+ for (ByteString s : clearPasswords)
+ {
+ if (Arrays.equals(s.value(), pw.value()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ clearPasswords.add(pw);
+ }
+ }
+ }
+ }
+
for (AttributeValue v : newPasswords)
{
StringBuilder invalidReason = new StringBuilder();
if (! pwPolicyState.passwordIsAcceptable(this, modifiedEntry,
v.getValue(),
+ clearPasswords,
invalidReason))
{
setResultCode(ResultCode.UNWILLING_TO_PERFORM);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
index 4307311..c30c17f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -36,6 +36,7 @@
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import org.opends.server.api.AccountStatusNotificationHandler;
import org.opends.server.api.PasswordGenerator;
@@ -3758,17 +3759,22 @@
* Indicates whether the provided password appears to be acceptable according
* to the password validators.
*
- * @param operation The operation that provided the password.
- * @param userEntry The user entry in which the password is used.
- * @param password The password to be validated.
- * @param invalidReason A buffer that may be used to hold the invalid reason
- * if the password is rejected.
+ * @param operation The operation that provided the password.
+ * @param userEntry The user entry in which the password is used.
+ * @param newPassword The password to be validated.
+ * @param currentPasswords The set of clear-text current passwords for the
+ * user (this may be a subset if not all of them are
+ * available in the clear, or empty if none of them
+ * are available in the clear).
+ * @param invalidReason A buffer that may be used to hold the invalid
+ * reason if the password is rejected.
*
* @return <CODE>true</CODE> if the password is acceptable for use, or
* <CODE>false</CODE> if it is not.
*/
public boolean passwordIsAcceptable(Operation operation, Entry userEntry,
- ByteString password,
+ ByteString newPassword,
+ Set<ByteString> currentPasswords,
StringBuilder invalidReason)
{
assert debugEnter(CLASS_NAME, "passwordIsAcceptable",
@@ -3780,8 +3786,8 @@
PasswordValidator validator =
passwordPolicy.getPasswordValidators().get(validatorDN);
- if (! validator.passwordIsValid(password, operation, userEntry,
- invalidReason))
+ if (! validator.passwordIsAcceptable(newPassword, currentPasswords,
+ operation, userEntry, invalidReason))
{
if (debug)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/LengthBasedPasswordValidator.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/LengthBasedPasswordValidator.java
index 15c76b5..8bad509 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/LengthBasedPasswordValidator.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/LengthBasedPasswordValidator.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.PasswordValidator;
@@ -98,19 +99,9 @@
/**
- * Initializes this password validator based on the information in the
- * provided configuration entry.
- *
- * @param configEntry The configuration entry that contains the information
- * to use to initialize this password validator.
- *
- * @throws ConfigException If an unrecoverable problem arises in the
- * process of performing the initialization.
- *
- * @throws InitializationException If a problem occurs during initialization
- * that is not related to the server
- * configuration.
+ * {@inheritDoc}
*/
+ @Override()
public void initializePasswordValidator(ConfigEntry configEntry)
throws ConfigException, InitializationException
{
@@ -190,9 +181,9 @@
/**
- * Performs any finalization that might be required when this password
- * validator is unloaded. No action is taken in the default implementation.
+ * {@inheritDoc}
*/
+ @Override()
public void finalizePasswordValidator()
{
assert debugEnter(CLASS_NAME, "finalizePasswordValidator");
@@ -203,33 +194,21 @@
/**
- * Indicates whether the provided password is acceptable for use by the
- * specified user. If the password is determined to be unacceptable, then a
- * human-readable explanation should be appended to the provided buffer.
- *
- * @param password The proposed clear-text password that should be
- * validated.
- * @param operation The operation that is being used to set the
- * password.
- * @param userEntry The entry for the user whose password is being
- * changed.
- * @param invalidReason The buffer to which the human-readable explanation
- * should be appended if it is determined that the
- * password is not acceptable.
- *
- * @return <CODE>true</CODE> if the password is acceptable, or
- * <CODE>false</CODE> if not.
+ * {@inheritDoc}
*/
- public boolean passwordIsValid(ByteString password, Operation operation,
- Entry userEntry, StringBuilder invalidReason)
+ @Override()
+ public boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
+ Operation operation, Entry userEntry,
+ StringBuilder invalidReason)
{
- assert debugEnter(CLASS_NAME, "passwordIsValid",
+ assert debugEnter(CLASS_NAME, "passwordIsAcceptable",
"org.opends.server.protocols.asn1.ASN1OctetString",
String.valueOf(operation), String.valueOf(userEntry),
"java.lang.StringBuilder");
- int numChars = password.stringValue().length();
+ int numChars = newPassword.stringValue().length();
if ((minLength > 0) && (numChars < minLength))
{
@@ -251,11 +230,7 @@
/**
- * Retrieves the DN of the configuration entry with which this component is
- * associated.
- *
- * @return The DN of the configuration entry with which this component is
- * associated.
+ * {@inheritDoc}
*/
public DN getConfigurableComponentEntryDN()
{
@@ -267,11 +242,7 @@
/**
- * Retrieves the set of configuration attributes that are associated with this
- * configurable component.
- *
- * @return The set of configuration attributes that are associated with this
- * configurable component.
+ * {@inheritDoc}
*/
public List<ConfigAttribute> getConfigurationAttributes()
{
@@ -296,18 +267,7 @@
/**
- * Indicates whether the provided configuration entry has an acceptable
- * configuration for this component. If it does not, then detailed
- * information about the problem(s) should be added to the provided list.
- *
- * @param configEntry The configuration entry for which to make the
- * determination.
- * @param unacceptableReasons A list that can be used to hold messages about
- * why the provided entry does not have an
- * acceptable configuration.
- *
- * @return <CODE>true</CODE> if the provided entry has an acceptable
- * configuration for this component, or <CODE>false</CODE> if not.
+ * {@inheritDoc}
*/
public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
List<String> unacceptableReasons)
@@ -387,20 +347,7 @@
/**
- * Makes a best-effort attempt to apply the configuration contained in the
- * provided entry. Information about the result of this processing should be
- * added to the provided message list. Information should always be added to
- * this list if a configuration change could not be applied. If detailed
- * results are requested, then information about the changes applied
- * successfully (and optionally about parameters that were not changed) should
- * also be included.
- *
- * @param configEntry The entry containing the new configuration to
- * apply for this component.
- * @param detailedResults Indicates whether detailed information about the
- * processing should be added to the list.
- *
- * @return Information about the result of the configuration update.
+ * {@inheritDoc}
*/
public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
boolean detailedResults)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
index 664c67c..82f4b81 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -29,9 +29,11 @@
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.HashSet;
import java.util.concurrent.locks.Lock;
import org.opends.server.api.ClientConnection;
@@ -685,9 +687,29 @@
{
if (selfChange || (! pwPolicyState.skipValidationForAdministrators()))
{
+ HashSet<ByteString> clearPasswords;
+ if (oldPassword == null)
+ {
+ clearPasswords =
+ new HashSet<ByteString>(pwPolicyState.getClearPasswords());
+ }
+ else
+ {
+ clearPasswords = new HashSet<ByteString>();
+ clearPasswords.add(oldPassword);
+ for (ByteString pw : pwPolicyState.getClearPasswords())
+ {
+ if (! Arrays.equals(pw.value(), oldPassword.value()))
+ {
+ clearPasswords.add(pw);
+ }
+ }
+ }
+
StringBuilder invalidReason = new StringBuilder();
if (! pwPolicyState.passwordIsAcceptable(operation, userEntry,
newPassword,
+ clearPasswords,
invalidReason))
{
if (oldPassword == null)
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif b/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
index 2c7307c..dcb8836 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
+++ b/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -21,6 +21,19 @@
replace: ds-cfg-suppress-internal-operations
ds-cfg-suppress-internal-operations: false
+dn: cn=Test Password Validator,cn=Password Validators,cn=config
+changetype: add
+objectClass: top
+objectClass: ds-cfg-password-validator
+cn: Test Password Validator
+ds-cfg-password-validator-class: org.opends.server.extensions.TestPasswordValidator
+ds-cfg-password-validator-enabled: true
+
+dn: cn=Default Password Policy,cn=Password Policies,cn=config
+changetype: modify
+add: ds-cfg-password-validator-dn
+ds-cfg-password-validator-dn: cn=Test Password Validator,cn=Password Validators,cn=config
+
dn: cn=SSHA512 UserPassword Policy,cn=Password Policies,cn=config
changetype: add
objectClass: top
@@ -49,6 +62,7 @@
ds-cfg-require-secure-authentication: false
ds-cfg-require-secure-password-changes: false
ds-cfg-skip-validation-for-administrators: false
+ds-cfg-password-validator-dn: cn=Test Password Validator,cn=Password Validators,cn=config
dn: cn=SHA1 AuthPassword Policy,cn=Password Policies,cn=config
changetype: add
@@ -78,6 +92,7 @@
ds-cfg-require-secure-authentication: false
ds-cfg-require-secure-password-changes: false
ds-cfg-skip-validation-for-administrators: false
+ds-cfg-password-validator-dn: cn=Test Password Validator,cn=Password Validators,cn=config
dn: cn=Clear UserPassword Policy,cn=Password Policies,cn=config
changetype: add
@@ -107,6 +122,7 @@
ds-cfg-require-secure-authentication: false
ds-cfg-require-secure-password-changes: false
ds-cfg-skip-validation-for-administrators: false
+ds-cfg-password-validator-dn: cn=Test Password Validator,cn=Password Validators,cn=config
dn: cn=Delay PreOperation Plugin,cn=Plugins,cn=config
changetype: add
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/PasswordValidatorTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/PasswordValidatorTestCase.java
new file mode 100644
index 0000000..140fdff
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/PasswordValidatorTestCase.java
@@ -0,0 +1,866 @@
+/*
+ * 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 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+
+
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperation;
+import org.opends.server.extensions.TestPasswordValidator;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.asn1.ASN1Reader;
+import org.opends.server.protocols.asn1.ASN1Sequence;
+import org.opends.server.protocols.asn1.ASN1Writer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.BindRequestProtocolOp;
+import org.opends.server.protocols.ldap.BindResponseProtocolOp;
+import org.opends.server.protocols.ldap.LDAPAttribute;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.protocols.ldap.ModifyRequestProtocolOp;
+import org.opends.server.protocols.ldap.ModifyResponseProtocolOp;
+import org.opends.server.tools.LDAPPasswordModify;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of generic test cases for password validators.
+ */
+public class PasswordValidatorTestCase
+ extends APITestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Gets simple test coverage for the default
+ * PasswordValidator.finalizePasswordValidator method.
+ */
+ @Test()
+ public void testFinalizePasswordValidator()
+ {
+ TestPasswordValidator.getInstance().finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Performs a test to ensure that the password validation will be successful
+ * under the base conditions for the password modify extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulValidationPasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-c", "password",
+ "-n", "newPassword"
+ };
+ assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+ 0);
+
+ assertEquals(TestPasswordValidator.getLastNewPassword().value(),
+ new ASN1OctetString("newPassword").value());
+ assertFalse(TestPasswordValidator.getLastCurrentPasswords().isEmpty());
+ }
+
+
+
+ /**
+ * Performs a test to ensure that the password validation will fail if the
+ * test validator is configured to make it fail for the password modify
+ * extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedValidationPasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ TestPasswordValidator.setNextReturnValue(false);
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-c", "password",
+ "-n", "newPassword"
+ };
+
+ int returnCode = LDAPPasswordModify.mainPasswordModify(args, false, null,
+ null);
+ assertFalse(returnCode == 0);
+
+ assertEquals(TestPasswordValidator.getLastNewPassword().value(),
+ new ASN1OctetString("newPassword").value());
+ assertFalse(TestPasswordValidator.getLastCurrentPasswords().isEmpty());
+
+ TestPasswordValidator.setNextReturnValue(true);
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will not be
+ * provided if the user has a non-reversible scheme and does not provide the
+ * current password for a password modify extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testCurrentPasswordNotAvailablePasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-n", "newPassword"
+ };
+ assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+ 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertTrue(currentPasswords.isEmpty());
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a non-reversible scheme but provides the current password
+ * for a password modify extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testCurrentPasswordAvailablePasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-c", "password",
+ "-n", "newPassword"
+ };
+ assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+ 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a reversible scheme and does not provide the current
+ * password for a password modify extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStoredPasswordAvailablePasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password",
+ "pwdPolicySubentry: cn=Clear UserPassword Policy,cn=Password " +
+ "Policies,cn=config");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-n", "newPassword"
+ };
+ assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+ 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a reversible scheme and also provides the current password
+ * for a password modify extended operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStoredAndCurrentPasswordAvailablePasswordModifyExtOp()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password",
+ "pwdPolicySubentry: cn=Clear UserPassword Policy,cn=Password " +
+ "Policies,cn=config");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "uid=test.user,o=test",
+ "-w", "password",
+ "-c", "password",
+ "-n", "newPassword"
+ };
+ assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+ 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+
+
+
+ /**
+ * Performs a test to ensure that the password validation will be successful
+ * under the base conditions for the modify operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulValidationModify()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+ r.setIOTimeout(3000);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+ ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("newPassword"));
+ LDAPAttribute attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+ message = new LDAPMessage(2, modifyRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertEquals(modifyResponse.getResultCode(), 0);
+
+ assertEquals(TestPasswordValidator.getLastNewPassword().value(),
+ new ASN1OctetString("newPassword").value());
+ assertTrue(TestPasswordValidator.getLastCurrentPasswords().isEmpty());
+ }
+
+
+
+ /**
+ * Performs a test to ensure that the password validation will fail if the
+ * test validator is configured to make it fail for the modify operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedValidationModify()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+ r.setIOTimeout(3000);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+ ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("newPassword"));
+ LDAPAttribute attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+ TestPasswordValidator.setNextReturnValue(false);
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+ message = new LDAPMessage(2, modifyRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == 0);
+
+ assertEquals(TestPasswordValidator.getLastNewPassword().value(),
+ new ASN1OctetString("newPassword").value());
+ assertTrue(TestPasswordValidator.getLastCurrentPasswords().isEmpty());
+
+ TestPasswordValidator.setNextReturnValue(true);
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a non-reversible scheme but provides the current password
+ * for a modify operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testCurrentPasswordAvailableModify()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+ r.setIOTimeout(3000);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+ ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("password"));
+ LDAPAttribute attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.DELETE, attr));
+
+ values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("newPassword"));
+ attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.ADD, attr));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+ message = new LDAPMessage(2, modifyRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertEquals(modifyResponse.getResultCode(), 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a reversible scheme and does not provide the current
+ * password for a modify operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStoredPasswordAvailableModify()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password",
+ "pwdPolicySubentry: cn=Clear UserPassword Policy,cn=Password " +
+ "Policies,cn=config");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+ r.setIOTimeout(3000);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+ ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("newPassword"));
+ LDAPAttribute attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+ message = new LDAPMessage(2, modifyRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertEquals(modifyResponse.getResultCode(), 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+
+
+
+ /**
+ * Performs a test to make sure that the clear-text password will be provided
+ * if the user has a reversible scheme and also provides the current password
+ * for a modify operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStoredAndCurrentPasswordAvailableModify()
+ throws Exception
+ {
+ TestPasswordValidator.setNextReturnValue(true);
+ TestPasswordValidator.setNextInvalidReason(null);
+
+ 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: password",
+ "pwdPolicySubentry: cn=Clear UserPassword Policy,cn=Password " +
+ "Policies,cn=config");
+
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(userEntry.getDN(), userEntry.getObjectClasses(),
+ userEntry.getUserAttributes(),
+ userEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+ r.setIOTimeout(3000);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+ ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("password"));
+ LDAPAttribute attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.DELETE, attr));
+
+ values = new ArrayList<ASN1OctetString>();
+ values.add(new ASN1OctetString("newPassword"));
+ attr = new LDAPAttribute("userPassword", values);
+ mods.add(new LDAPModification(ModificationType.ADD, attr));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+ message = new LDAPMessage(2, modifyRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertEquals(modifyResponse.getResultCode(), 0);
+
+ Set<ByteString> currentPasswords =
+ TestPasswordValidator.getLastCurrentPasswords();
+ assertFalse(currentPasswords.isEmpty());
+ assertEquals(currentPasswords.size(), 1);
+ assertEquals(currentPasswords.iterator().next().value(),
+ new ASN1OctetString("password").value());
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
index 6b54dd5..7860581 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
@@ -2522,7 +2522,7 @@
{
PasswordPolicy p = DirectoryServer.getDefaultPasswordPolicy();
assertNotNull(p.getPasswordValidators());
- assertTrue(p.getPasswordValidators().isEmpty());
+ assertFalse(p.getPasswordValidators().isEmpty());
String dnStr = "cn=Default Password Policy,cn=Password Policies,cn=config";
String attr = "ds-cfg-password-validator-dn";
@@ -2567,7 +2567,7 @@
"cn=config");
PasswordPolicy p = DirectoryServer.getPasswordPolicy(dn);
assertNotNull(p.getPasswordValidators());
- assertTrue(p.getPasswordValidators().isEmpty());
+ assertFalse(p.getPasswordValidators().isEmpty());
String attr = "ds-cfg-password-validator-dn";
String valDN = "cn=Length-Based Password Validator," +
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java
index 3c57f29..614bc88 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import org.testng.annotations.BeforeClass;
@@ -43,6 +44,7 @@
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.Control;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -312,13 +314,13 @@
/**
- * Tests the <CODE>passwordIsValid</CODE> method with no constraints on
+ * Tests the <CODE>passwordIsAcceptable</CODE> method with no constraints on
* password length.
*
* @throws Exception If an unexpected problem occurs.
*/
@Test()
- public void testPasswordIsValidNoConstraints()
+ public void testPasswordIsAcceptableNoConstraints()
throws Exception
{
TestCaseUtils.initializeTestBackend(true);
@@ -374,8 +376,9 @@
DN.decode("cn=uid=test.user,o=test"), mods);
StringBuilder invalidReason = new StringBuilder();
- assertTrue(validator.passwordIsValid(password, op, userEntry,
- invalidReason));
+ assertTrue(validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0),
+ op, userEntry, invalidReason));
}
validator.finalizePasswordValidator();
@@ -384,13 +387,13 @@
/**
- * Tests the <CODE>passwordIsValid</CODE> method with a constraint on the
+ * Tests the <CODE>passwordIsAcceptable</CODE> method with a constraint on the
* minimum password length.
*
* @throws Exception If an unexpected problem occurs.
*/
@Test()
- public void testPasswordIsValidMinLengthConstraint()
+ public void testPasswordIsAcceptableMinLengthConstraint()
throws Exception
{
TestCaseUtils.initializeTestBackend(true);
@@ -447,8 +450,10 @@
StringBuilder invalidReason = new StringBuilder();
assertEquals((buffer.length() >= 10),
- validator.passwordIsValid(password, op, userEntry,
- invalidReason));
+ validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0),
+ op, userEntry,
+ invalidReason));
}
validator.finalizePasswordValidator();
@@ -457,13 +462,13 @@
/**
- * Tests the <CODE>passwordIsValid</CODE> method with a constraint on the
+ * Tests the <CODE>passwordIsAcceptable</CODE> method with a constraint on the
* maximum password length.
*
* @throws Exception If an unexpected problem occurs.
*/
@Test()
- public void testPasswordIsValidMaxLengthConstraint()
+ public void testPasswordIsAcceptableMaxLengthConstraint()
throws Exception
{
TestCaseUtils.initializeTestBackend(true);
@@ -520,8 +525,10 @@
StringBuilder invalidReason = new StringBuilder();
assertEquals((buffer.length() <= 10),
- validator.passwordIsValid(password, op, userEntry,
- invalidReason));
+ validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0),
+ op, userEntry,
+ invalidReason));
}
validator.finalizePasswordValidator();
@@ -530,13 +537,13 @@
/**
- * Tests the <CODE>passwordIsValid</CODE> method with constraints on both the
- * minimum and maximum password length.
+ * Tests the <CODE>passwordIsAcceptable</CODE> method with constraints on both
+ * the minimum and maximum password length.
*
* @throws Exception If an unexpected problem occurs.
*/
@Test()
- public void testPasswordIsValidMinAndMaxLengthConstraints()
+ public void testPasswordIsAcceptableMinAndMaxLengthConstraints()
throws Exception
{
TestCaseUtils.initializeTestBackend(true);
@@ -593,8 +600,10 @@
StringBuilder invalidReason = new StringBuilder();
assertEquals(((buffer.length() >= 6) && (buffer.length() <= 10)),
- validator.passwordIsValid(password, op, userEntry,
- invalidReason));
+ validator.passwordIsAcceptable(password,
+ new HashSet<ByteString>(0),
+ op, userEntry,
+ invalidReason));
}
validator.finalizePasswordValidator();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/TestPasswordValidator.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/TestPasswordValidator.java
new file mode 100644
index 0000000..a4af91a
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/TestPasswordValidator.java
@@ -0,0 +1,237 @@
+/*
+ * 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 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Set;
+
+import org.opends.server.api.PasswordValidator;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.core.Operation;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+
+
+
+/**
+ * This class provides a very simple password validator that can be used for
+ * testing purposes. It provides the ability to inspect the arguments provided
+ * to the password validator, as well as to manipulate the result that it will
+ * return.
+ */
+public class TestPasswordValidator
+ extends PasswordValidator
+{
+ /**
+ * The singleton instance of this test password validator.
+ */
+ private static TestPasswordValidator instance = null;
+
+
+
+ // The next value to return from the passwordIsAcceptable method.
+ private boolean nextReturnValue;
+
+ // The last new password provided to the passwordIsAcceptable method.
+ private ByteString lastNewPassword;
+
+ // The last user entry provided to the passwordIsAcceptable method.
+ private Entry lastUserEntry;
+
+ // The last operation provided to the passwordIsAcceptable method.
+ private Operation lastOperation;
+
+ // The last set of current passwords provided to the passwordIsAcceptable
+ // method.
+ private Set<ByteString> lastCurrentPasswords;
+
+ // The next invalid reason that should be used in the passwordIsAcceptable
+ // method.
+ private String nextInvalidReason;
+
+
+
+ /**
+ * Creates a new instance of this password validator.
+ */
+ public TestPasswordValidator()
+ {
+ super();
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializePasswordValidator(ConfigEntry configEntry)
+ throws InitializationException
+ {
+ if (instance == null)
+ {
+ instance = this;
+ }
+ else
+ {
+ throw new InitializationException(1,
+ "Cannot configure more than one TestPasswordValidator instance");
+ }
+
+ lastNewPassword = null;
+ lastCurrentPasswords = null;
+ lastOperation = null;
+ lastUserEntry = null;
+ nextReturnValue = true;
+ nextInvalidReason = null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
+ Operation operation, Entry userEntry,
+ StringBuilder invalidReason)
+ {
+ lastNewPassword = newPassword;
+ lastCurrentPasswords = currentPasswords;
+ lastOperation = operation;
+ lastUserEntry = userEntry;
+
+ if (nextInvalidReason != null)
+ {
+ invalidReason.append(nextInvalidReason);
+ }
+
+ return nextReturnValue;
+ }
+
+
+
+ /**
+ * Retrieves an instance of this test password validator.
+ *
+ * @return An instance of this test password validator, or <CODE>null</CODE>
+ * if no instance has been created.
+ */
+ public static TestPasswordValidator getInstance()
+ {
+ return instance;
+ }
+
+
+
+ /**
+ * Retrieves the last <CODE>newPassword</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ *
+ * @return The last <CODE>newPassword</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static ByteString getLastNewPassword()
+ {
+ return instance.lastNewPassword;
+ }
+
+
+
+ /**
+ * Retrieves the last <CODE>currentPasswords</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ *
+ * @return The last <CODE>currentPasswords</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static Set<ByteString> getLastCurrentPasswords()
+ {
+ return instance.lastCurrentPasswords;
+ }
+
+
+
+ /**
+ * Retrieves the last <CODE>operation</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ *
+ * @return The last <CODE>operation</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static Operation getLastOperation()
+ {
+ return instance.lastOperation;
+ }
+
+
+
+ /**
+ * Retrieves the last <CODE>userEntry</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ *
+ * @return The last <CODE>userEntry</CODE> value provided to the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static Entry getLastUserEntry()
+ {
+ return instance.lastUserEntry;
+ }
+
+
+
+ /**
+ * Sets the next value to return from the <CODE>passwordIsAcceptable</CODE>
+ * method.
+ *
+ * @param nextReturnValue The next value to return from the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static void setNextReturnValue(boolean nextReturnValue)
+ {
+ instance.nextReturnValue = nextReturnValue;
+ }
+
+
+
+ /**
+ * Sets the next string to append to the <CODE>invalidReason</CODE> buffer in
+ * the <CODE>passwordIsAcceptable</CODE> method.
+ *
+ * @param nextReturnValue The next string to append to the
+ * <CODE>invalidReason</CODE> buffer in the
+ * <CODE>passwordIsAcceptable</CODE> method.
+ */
+ public static void setNextInvalidReason(String nextInvalidReason)
+ {
+ instance.nextInvalidReason = nextInvalidReason;
+ }
+}
+
--
Gitblit v1.10.0