From 591624adc1b6a24fc0f6293cbd4e1122df816ac3 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 26 Jan 2009 13:11:18 +0000
Subject: [PATCH] Fix issue 3750 - Forcing a password change after admin reset causes unexpected behavior.

---
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java       |   22 ++++++----
 opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java |   86 +++++++++++++++++++++++++++++++++++++------
 2 files changed, 87 insertions(+), 21 deletions(-)

diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
index dc83bc6..82d0f6e 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.workflowelement.localbackend;
 
@@ -282,6 +282,7 @@
    * @throws  DirectoryException  If an unexpected problem occurs while applying
    *                              the modification to the entry.
    */
+  @Override
   public void addModification(Modification modification)
     throws DirectoryException
   {
@@ -529,15 +530,18 @@
         }
 
 
-        if ((! passwordChanged) && (! isInternalOperation()) &&
-            pwPolicyState.mustChangePassword())
+        DN authzDN = getAuthorizationDN();
+        if ((!passwordChanged) && (!isInternalOperation())
+            && pwPolicyState.mustChangePassword())
         {
-          // The user will not be allowed to do anything else before the
-          // password gets changed.
-          pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
-          setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-          appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
-          break modifyProcessing;
+          if (authzDN != null && authzDN.equals(entryDN))
+          {
+            // The user did not attempt to change their password.
+            pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+            setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+            appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
+            break modifyProcessing;
+          }
         }
 
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java
index c481652..d54e352 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.controls;
 
@@ -33,6 +33,7 @@
 import java.util.LinkedHashSet;
 
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import org.opends.server.TestCaseUtils;
@@ -653,18 +654,65 @@
 
 
 
+
   /**
-   * Tests that an appropriate password policy response control is returned for
-   * a modify operation when the user's password is in a "must change" state.
+   * Creates test data for testModifyMustChange.
    *
-   * @throws  Exception  If an unexpected problem occurs.
+   * Fields:
+   *   <userDN> <entryDN> <changeAfterReset>
+   *
+   * @return Returns test data for testModifyMustChange.
    */
-  @Test()
-  public void testModifyMustChange()
+  @DataProvider(name = "testModifyMustChange")
+  public Object[][] createTestModifyMustChange() {
+    return new Object[][] {
+        // User does not need to change their password.
+        { "uid=test.admin,o=test", "uid=test.admin,o=test", false },
+        { "uid=test.admin,o=test", "uid=test.user,o=test",  false },
+        { "uid=test.admin,o=test", "o=test",                false },
+
+        // User does need to change their password.
+        { "uid=test.user,o=test",  "uid=test.admin,o=test", true },
+        { "uid=test.user,o=test",  "uid=test.user,o=test",  true },
+        { "uid=test.user,o=test",  "o=test",                true }
+    };
+  }
+
+
+
+  /**
+   * Tests that an appropriate password policy response control is
+   * returned for a modify operation when the user's password is in a
+   * "must change" state.
+   *
+   * @param userDN
+   *          The name of the user to bind as.
+   * @param entryDN
+   *          The name of the entry to modify.
+   * @param changeAfterReset
+   *          {@code true} if change after reset is expected.
+   * @throws Exception
+   *           If an unexpected problem occurs.
+   */
+  @Test(dataProvider="testModifyMustChange")
+  public void testModifyMustChange(String userDN, String entryDN, boolean changeAfterReset)
          throws Exception
   {
     TestCaseUtils.initializeTestBackend(true);
 
+    TestCaseUtils.addEntry(
+        "dn: uid=test.admin,o=test",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.admin",
+        "givenName: Test Admin",
+        "sn: Admin",
+        "cn: Test Admin",
+        "userPassword: password",
+        "ds-privilege-name: bypass-acl");
+
     TestCaseUtils.dsconfig(
       "set-password-policy-prop",
       "--policy-name", "Default Password Policy",
@@ -690,7 +738,7 @@
     try
     {
       BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
-           new ASN1OctetString("uid=test.user,o=test"), 3,
+           new ASN1OctetString(userDN), 3,
            new ASN1OctetString("password"));
       LDAPMessage message = new LDAPMessage(1, bindRequest);
       w.writeElement(message.encode());
@@ -705,7 +753,7 @@
                                       "foo"));
 
       ModifyRequestProtocolOp modifyRequest =
-           new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+           new ModifyRequestProtocolOp(new ASN1OctetString(entryDN), mods);
 
       ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
       controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
@@ -716,7 +764,17 @@
       message = LDAPMessage.decode(r.readElement().decodeAsSequence());
       ModifyResponseProtocolOp modifyResponse =
            message.getModifyResponseProtocolOp();
-      assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+      if (changeAfterReset)
+      {
+        assertEquals(modifyResponse.getResultCode(),
+            LDAPResultCode.UNWILLING_TO_PERFORM);
+      }
+      else
+      {
+        assertEquals(modifyResponse.getResultCode(),
+            LDAPResultCode.SUCCESS);
+      }
 
       controls = message.getControls();
       assertNotNull(controls);
@@ -729,8 +787,12 @@
         {
           PasswordPolicyResponseControl pwpControl =
                PasswordPolicyResponseControl.decodeControl(c.getControl());
-          assertEquals(pwpControl.getErrorType(),
-                       PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+          if (changeAfterReset) {
+            assertEquals(pwpControl.getErrorType(),
+                         PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+          } else {
+            assertNull(pwpControl.getErrorType());
+          }
           found = true;
         }
       }
@@ -759,7 +821,7 @@
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  @Test()
+  @Test
   public void testModifyCannotChange()
          throws Exception
   {

--
Gitblit v1.10.0