This method will instantly carry out an update() after the
move, so the entry is moved instantly.
You can pass an optional Horde_Ldap object. In this case, a
cross directory move will be performed which deletes the entry
in the source (THIS) directory and adds it in the directory
$target_ldap.
A cross directory move will switch the entry's internal LDAP
reference so updates to the entry will go to the new directory.
If you want to do a cross directory move, you need to pass an
Horde_Ldap_Entry object, otherwise the attributes will be
empty.
/** * Update a set of authentication credentials. * * @todo Clean this up for Horde 5. * * @param string $oldID The old userId. * @param string $newID The new userId. * @param array $credentials The new credentials. * @param string $olddn The old user DN. * @param string $newdn The new user DN. * * @throws Horde_Auth_Exception */ public function updateUser($oldID, $newID, $credentials, $olddn = null, $newdn = null) { if (!empty($this->_params['ad'])) { throw new Horde_Auth_Exception(__CLASS__ . ': Updating users is not supported for Active Directory.'); } if (is_null($olddn)) { /* Search for the user's full DN. */ try { $dn = $this->_ldap->findUserDN($oldID); } catch (Horde_Exception_Ldap $e) { throw new Horde_Auth_Exception($e); } $olddn = $dn; $newdn = preg_replace('/uid=.*?,/', 'uid=' . $newID . ',', $dn, 1); $shadow = $this->_lookupShadow($dn); /* If shadowmin hasn't yet expired only change when we are administrator */ if ($shadow['shadowlastchange'] && $shadow['shadowmin'] && $shadow['shadowlastchange'] + $shadow['shadowmin'] > time() / 86400) { throw new Horde_Auth_Exception('Minimum password age has not yet expired'); } /* Set the lastchange field */ if ($shadow['shadowlastchange']) { $entry['shadowlastchange'] = floor(time() / 86400); } /* Encrypt the new password */ $entry['userpassword'] = Horde_Auth::getCryptedPassword($credentials['password'], '', $this->_params['encryption'], 'true'); } else { $entry = $credentials; unset($entry['dn']); } try { if ($oldID != $newID) { $this->_ldap->move($olddn, $newdn); $this->_ldap->modify($newdn, array('replace' => $entry)); } else { $this->_ldap->modify($olddn, array('replace' => $entry)); } } catch (Horde_Ldap_Exception $e) { throw new Horde_Auth_Exception(sprintf(__CLASS__ . ': Unable to update user "%s"', $newID)); } }
/** * Test move(). */ public function testMove() { $ldap = new Horde_Ldap(self::$ldapcfg['server']); // For Moving tests, we need some little tree again. $base = self::$ldapcfg['server']['basedn']; $testdn = 'ou=Horde_Ldap_Test_move,' . $base; $ou = Horde_Ldap_Entry::createFresh($testdn, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'Horde_Ldap_Test_move')); $ou_1 = Horde_Ldap_Entry::createFresh('ou=source,' . $testdn, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'source')); $ou_1_l1 = Horde_Ldap_Entry::createFresh('l=moveitem,ou=source,' . $testdn, array('objectClass' => array('top', 'locality'), 'l' => 'moveitem', 'description' => 'movetest')); $ou_2 = Horde_Ldap_Entry::createFresh('ou=target,' . $testdn, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'target')); $ou_3 = Horde_Ldap_Entry::createFresh('ou=target_otherdir,' . $testdn, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'target_otherdir')); $ldap->add($ou); $ldap->add($ou_1); $ldap->add($ou_1_l1); $ldap->add($ou_2); $ldap->add($ou_3); $this->assertTrue($ldap->exists($ou->dn())); $this->assertTrue($ldap->exists($ou_1->dn())); $this->assertTrue($ldap->exists($ou_1_l1->dn())); $this->assertTrue($ldap->exists($ou_2->dn())); $this->assertTrue($ldap->exists($ou_3->dn())); // Tree established. // Local rename. $olddn = $ou_1_l1->currentDN(); $ldap->move($ou_1_l1, str_replace('moveitem', 'move_item', $ou_1_l1->dn())); $this->assertTrue($ldap->exists($ou_1_l1->dn())); $this->assertFalse($ldap->exists($olddn)); // Local move. $olddn = $ou_1_l1->currentDN(); $ldap->move($ou_1_l1, 'l=move_item,' . $ou_2->dn()); $this->assertTrue($ldap->exists($ou_1_l1->dn())); $this->assertFalse($ldap->exists($olddn)); // Local move backward, with rename. Here we use the DN of the object, // to test DN conversion. // Note that this will outdate the object since it does not has // knowledge about the move. $olddn = $ou_1_l1->currentDN(); $newdn = 'l=moveditem,' . $ou_2->dn(); $ldap->move($olddn, $newdn); $this->assertTrue($ldap->exists($newdn)); $this->assertFalse($ldap->exists($olddn)); // Refetch since the object's DN was outdated. $ou_1_l1 = $ldap->getEntry($newdn); // Fake-cross directory move using two separate links to the same // directory. This other directory is represented by // ou=target_otherdir. $ldap2 = new Horde_Ldap(self::$ldapcfg['server']); $olddn = $ou_1_l1->currentDN(); $ldap->move($ou_1_l1, 'l=movedcrossdir,' . $ou_3->dn(), $ldap2); $this->assertFalse($ldap->exists($olddn)); $this->assertTrue($ldap2->exists($ou_1_l1->dn())); // Try to move over an existing entry. try { $ldap->move($ou_2, $ou_3->dn(), $ldap2); $this->fail('Horde_Ldap_Exception expected.'); } catch (Horde_Ldap_Exception $e) { } // Try cross directory move without providing an valid entry but a DN. try { $ldap->move($ou_1_l1->dn(), 'l=movedcrossdir2,' . $ou_2->dn(), $ldap2); $this->fail('Horde_Ldap_Exception expected.'); } catch (Horde_Ldap_Exception $e) { } // Try passing an invalid entry object. try { $ldap->move($ldap, 'l=move_item,' . $ou_2->dn()); $this->fail('Horde_Ldap_Exception expected.'); } catch (Horde_Ldap_Exception $e) { } // Try passing an invalid LDAP object. try { $ldap->move($ou_1_l1, 'l=move_item,' . $ou_2->dn(), $ou_1); $this->fail('Horde_Ldap_Exception expected.'); } catch (Horde_Ldap_Exception $e) { } }