From 7f458a2fe3eb08a8f63cc8d914a65b903d948ab4 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Mon, 14 Jul 2008 17:02:14 +0000
Subject: [PATCH] - [ Issue 3468 ] Password ext op: userIdentity should accept a DN.

---
 opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java                                 |   52 ++++++++++--
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java |  130 ++++++++++++++++++++++++++++++++
 opends/src/messages/messages/extension.properties                                                                   |    5 
 3 files changed, 175 insertions(+), 12 deletions(-)

diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index 237fe2e..6467791 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -99,9 +99,8 @@
  request cannot be processed because the server cannot decode "%s" as a valid \
  DN for use in the authorization ID for the operation
 MILD_ERR_EXTOP_PASSMOD_INVALID_AUTHZID_STRING_37=The password modify extended \
- request cannot be processed because it contained an invalid authorization ID \
- that did not start with either "dn:" or "u:".  The provided authorization ID \
- string was "%s"
+ request cannot be processed because it contained an invalid userIdentity \
+ field.  The provided userIdentity string was "%s"
 MILD_ERR_EXTOP_PASSMOD_NO_USER_ENTRY_BY_AUTHZID_38=The password modify \
  extended request cannot be processed because it was not possible to identify \
  the user entry to update based on the authorization DN of "%s"
diff --git a/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java b/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
index 4c15ec8..a1a4788 100644
--- a/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
+++ b/opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -319,7 +319,7 @@
     // See if a user identity was provided.  If so, then try to resolve it to
     // an actual user.
     DN    userDN    = null;
-    Entry userEntry;
+    Entry userEntry = null;
     Lock  userLock  = null;
 
     try
@@ -372,9 +372,7 @@
       }
       else
       {
-        // There was a userIdentity section in the request.  It should have
-        // started with either "dn:" to indicate that it contained a DN, or
-        // "u:" to indicate that it contained a user ID.
+        // There was a userIdentity field in the request.
         String authzIDStr      = userIdentity.stringValue();
         String lowerAuthzIDStr = toLowerCase(authzIDStr);
         if (lowerAuthzIDStr.startsWith("dn:"))
@@ -468,15 +466,51 @@
             return;
           }
         }
+        // the userIdentity provided does not follow Authorization Identity
+        // form. RFC3062 declaration "may or may not be an LDAPDN" allows
+        // for pretty much anything in that field. we gonna try to parse it
+        // as DN first then if that fails as user ID.
         else
         {
-          // The authorization ID was in an illegal format.
-          operation.setResultCode(ResultCode.PROTOCOL_ERROR);
+          try
+          {
+            userDN = DN.decode(authzIDStr);
+          }
+          catch (DirectoryException de)
+          {
+            // IGNORE.
+          }
 
-          operation.appendErrorMessage(
-                  ERR_EXTOP_PASSMOD_INVALID_AUTHZID_STRING.get(authzIDStr));
+          if ((userDN != null) && (!userDN.isNullDN())) {
+            // If the provided DN is an alternate DN for a root user,
+            // then replace it with the actual root DN.
+            DN actualRootDN = DirectoryServer.getActualRootBindDN(userDN);
+            if (actualRootDN != null) {
+              userDN = actualRootDN;
+            }
+            userEntry = getEntryByDN(operation, userDN);
+          } else {
+            try
+            {
+              userEntry = identityMapper.getEntryForID(authzIDStr);
+            }
+            catch (DirectoryException de)
+            {
+              // IGNORE.
+            }
+          }
 
-          return;
+          if (userEntry == null) {
+            // The userIdentity was invalid.
+            operation.setResultCode(ResultCode.PROTOCOL_ERROR);
+            operation.appendErrorMessage(
+              ERR_EXTOP_PASSMOD_INVALID_AUTHZID_STRING.get(authzIDStr));
+            return;
+          }
+          else
+          {
+            userDN = userEntry.getDN();
+          }
         }
       }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java
index 1e83833..cc6ff57 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordModifyExtendedOperationTestCase.java
@@ -651,6 +651,136 @@
    * following configuration:
    * <BR>
    * <UL>
+   *   <LI>Authenticated as a normal user</LI>
+   *   <LI>userIdentity provided (LDAPDN form)</LI>
+   *   <LI>No current password provided</LI>
+   *   <LI>New password provided</LI>
+   * </UL>
+   *
+   * @throws  Exception  If an unexpected error occurs.
+   */
+  @Test()
+  public void testAsUserExplicitDNSelfNoOldPasswordWithNewPassword()
+         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",
+         "ds-privilege-name: bypass-acl",
+         "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 =
+    {
+      "--noPropertiesFile",
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "uid=test.user,o=test",
+      "-w", "password",
+      "-a", "uid=test.user,o=test",
+      "-n", "newPassword"
+    };
+    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+                 0);
+
+    // Perform an internal bind to verify the password was actually changed.
+    conn = new InternalClientConnection(new AuthenticationInfo());
+    BindOperation bindOperation =
+         conn.processSimpleBind(userEntry.getDN(),
+                                new ASN1OctetString("newPassword"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests the password modify extended operation over LDAP.  It will use the
+   * following configuration:
+   * <BR>
+   * <UL>
+   *   <LI>Authenticated as a normal user</LI>
+   *   <LI>userIdentity provided (userID form)</LI>
+   *   <LI>No current password provided</LI>
+   *   <LI>New password provided</LI>
+   * </UL>
+   *
+   * @throws  Exception  If an unexpected error occurs.
+   */
+  @Test()
+  public void testAsUserExplicitUSelfNoOldPasswordWithNewPassword()
+         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",
+         "ds-privilege-name: bypass-acl",
+         "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 =
+    {
+      "--noPropertiesFile",
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "uid=test.user,o=test",
+      "-w", "password",
+      "-a", "test.user",
+      "-n", "newPassword"
+    };
+    assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null),
+                 0);
+
+    // Perform an internal bind to verify the password was actually changed.
+    conn = new InternalClientConnection(new AuthenticationInfo());
+    BindOperation bindOperation =
+         conn.processSimpleBind(userEntry.getDN(),
+                                new ASN1OctetString("newPassword"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests the password modify extended operation over LDAP.  It will use the
+   * following configuration:
+   * <BR>
+   * <UL>
    *   <LI>Unauthenticated client connection</LI>
    *   <LI>Authorization ID provided ("dn:" form)</LI>
    *   <LI>Current password provided</LI>

--
Gitblit v1.10.0