This class servers some functionality to the other classes of Horde_Ldap but most of the methods can be used separately as well. Copyright 2009 Benedikt Hallinger Copyright 2010-2016 Horde LLC (http://www.horde.org/)
Author: Benedikt Hallinger (beni@php.net)
Author: Jan Schneider (jan@horde.org)
Esempio n. 1
0
 /**
  * Returns the win32 AD epoch number of days the password may be unchanged.
  *
  * @return integer|boolean  Number of days or false if no limit.
  */
 protected function _getMaxPasswd()
 {
     $dn = Horde_Ldap_Util::explodeDN($this->_params['basedn']);
     $domaindn = array();
     foreach ($dn as $rdn) {
         $attribute = Horde_Ldap_Util::splitAttributeString($rdn);
         if ($attribute[0] == 'DC') {
             $domaindn[] = $rdn;
         }
     }
     $dn = Horde_Ldap_Util::canonicalDN($domaindn);
     $search = $this->_ldap->search($domaindn, 'objectClass=*');
     $entry = $search->shiftEntry();
     try {
         return $entry->getValue('maxPwdAge', 'single');
     } catch (Horde_Ldap_Exception $e) {
         return false;
     }
 }
Esempio n. 2
0
 /**
  * Returns whether a DN exists in the directory.
  *
  * @param string|Horde_Ldap_Entry $dn The DN of the object to test.
  *
  * @return boolean  True if the DN exists.
  * @throws Horde_Ldap_Exception
  */
 public function exists($dn)
 {
     if ($dn instanceof Horde_Ldap_Entry) {
         $dn = $dn->dn();
     }
     if (!is_string($dn)) {
         throw new Horde_Ldap_Exception('Parameter $dn is not a string nor an entry object!');
     }
     /* Make dn relative to parent. */
     $base = Horde_Ldap_Util::explodeDN($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false));
     $entry_rdn = array_shift($base);
     $base = Horde_Ldap_Util::canonicalDN($base);
     $result = @ldap_list($this->_link, $base, $entry_rdn, array(), 1, 1);
     if (@ldap_count_entries($this->_link, $result)) {
         return true;
     }
     if ($this->errorName(@ldap_errno($this->_link)) == 'LDAP_NO_SUCH_OBJECT') {
         return false;
     }
     if (@ldap_errno($this->_link)) {
         throw new Horde_Ldap_Exception(@ldap_error($this->_link), @ldap_errno($this->_link));
     }
     return false;
 }
Esempio n. 3
0
 /**
  * Updates the entry on the directory server.
  *
  * This will evaluate all changes made so far and send them to the
  * directory server.
  *
  * If you make changes to objectclasses wich have mandatory attributes set,
  * update() will currently fail. Remove the entry from the server and readd
  * it as new in such cases. This also will deal with problems with setting
  * structural object classes.
  *
  * @todo Entry rename with a DN containing special characters needs testing!
  *
  * @throws Horde_Ldap_Exception
  */
 public function update()
 {
     /* Ensure we have a valid LDAP object. */
     $ldap = $this->getLDAP();
     /* Get and check link. */
     $link = $ldap->getLink();
     if (!is_resource($link)) {
         throw new Horde_Ldap_Exception('Could not update entry: internal LDAP link is invalid');
     }
     /* Delete the entry. */
     if ($this->_delete) {
         return $ldap->delete($this);
     }
     /* New entry. */
     if ($this->_new) {
         $ldap->add($this);
         $this->_new = false;
         $this->_changes['add'] = array();
         $this->_changes['delete'] = array();
         $this->_changes['replace'] = array();
         $this->_original = $this->_attributes;
         return;
     }
     /* Rename/move entry. */
     if (!is_null($this->_newdn)) {
         if ($ldap->getVersion() != 3) {
             throw new Horde_Ldap_Exception('Renaming/Moving an entry is only supported in LDAPv3');
         }
         /* Make DN relative to parent (needed for LDAP rename). */
         $parent = Horde_Ldap_Util::explodeDN($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false));
         $child = array_shift($parent);
         /* Maybe the DN consist of a multivalued RDN, we must build the DN
          * in this case because the $child RDN is an array. */
         if (is_array($child)) {
             $child = Horde_Ldap_Util::canonicalDN($child);
         }
         $parent = Horde_Ldap_Util::canonicalDN($parent);
         /* Rename/move. */
         if (!@ldap_rename($link, $this->_dn, $child, $parent, true)) {
             throw new Horde_Ldap_Exception('Entry not renamed: ' . @ldap_error($link), @ldap_errno($link));
         }
         /* Reflect changes to local copy. */
         $this->_dn = $this->_newdn;
         $this->_newdn = null;
     }
     /* Carry out modifications to the entry. */
     foreach ($this->_changes['add'] as $attr => $value) {
         /* If attribute exists, add new values. */
         if ($this->exists($attr)) {
             if (!@ldap_mod_add($link, $this->dn(), array($attr => $value))) {
                 throw new Horde_Ldap_Exception('Could not add new values to attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
             }
         } else {
             /* New attribute. */
             if (!@ldap_modify($link, $this->dn(), array($attr => $value))) {
                 throw new Horde_Ldap_Exception('Could not add new attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
             }
         }
         unset($this->_changes['add'][$attr]);
     }
     foreach ($this->_changes['delete'] as $attr => $value) {
         /* In LDAPv3 you need to specify the old values for deleting. */
         if (is_null($value) && $ldap->getVersion() == 3) {
             $value = $this->_original[$attr];
         }
         if (!@ldap_mod_del($link, $this->dn(), array($attr => $value))) {
             throw new Horde_Ldap_Exception('Could not delete attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
         }
         unset($this->_changes['delete'][$attr]);
     }
     foreach ($this->_changes['replace'] as $attr => $value) {
         if (!@ldap_modify($link, $this->dn(), array($attr => $value))) {
             throw new Horde_Ldap_Exception('Could not replace attribute ' . $attr . ' values: ' . @ldap_error($link), @ldap_errno($link));
         }
         unset($this->_changes['replace'][$attr]);
     }
     /* All went well, so $_attributes (local copy) becomes $_original
      * (server). */
     $this->_original = $this->_attributes;
 }
Esempio n. 4
0
 /**
  * Test group fetching.
  *
  * @return NULL
  */
 public function testGetGroups()
 {
     foreach ($this->servers as $server) {
         $filter = '(&(objectClass=kolabGroupOfNames)(member=' . Horde_Ldap_Util::escapeFilterValue('cn=The Administrator,dc=example,dc=org') . '))';
         $result = $server->search($filter, array());
         $this->assertTrue(!empty($result));
         /*         $entry = $server->_firstEntry($result); */
         /*         $this->assertTrue(!empty($entry)); */
         /*         $uid = $server->_getDn($entry); */
         /*         $this->assertTrue(!empty($uid)); */
         /*         $entry = $server->_nextEntry($entry); */
         /*         $this->assertTrue(empty($entry)); */
         /*         $entries = $server->_getDns($result); */
         /*         $this->assertTrue(!empty($entries)); */
         $groups = $server->getGroups('cn=The Administrator,dc=example,dc=org');
         $this->assertTrue(!empty($groups));
         $groups = $server->getGroups($server->uidForIdOrMailOrAlias('*****@*****.**'));
         $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
         $groups = $server->getGroupAddresses($server->uidForIdOrMailOrAlias('*****@*****.**'));
         $this->assertContains('*****@*****.**', $groups);
         $groups = $server->getGroups($server->uidForIdOrMailOrAlias('*****@*****.**'));
         $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
         $groups = $server->getGroupAddresses($server->uidForIdOrMailOrAlias('*****@*****.**'));
         $this->assertContains('*****@*****.**', $groups);
         $groups = $server->getGroups('nobody');
         $this->assertTrue(empty($groups));
     }
 }
Esempio n. 5
0
File: Ldap.php Progetto: horde/horde
 /**
  * Get the parent GUID of this object.
  *
  * @param string $guid The GUID of the child.
  *
  * @return string the parent GUID of this object.
  */
 public function getParentGuid($guid)
 {
     try {
         $base = Horde_Ldap_Util::explodeDN($guid, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false));
         $id = array_shift($base);
         $parent = Horde_Ldap_Util::canonicalDN($base, array('casefold' => 'none'));
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Kolab_Server_Exception('Retrieving the parent object failed!', Horde_Kolab_Server_Exception::SYSTEM, $e);
     }
     return $parent;
 }
Esempio n. 6
0
 /**
  * Returns whether a DN exists in the directory.
  *
  * @param string|Horde_Ldap_Entry $dn The DN of the object to test.
  *
  * @return boolean  True if the DN exists.
  * @throws Horde_Ldap_Exception
  */
 public function exists($dn)
 {
     if ($dn instanceof Horde_Ldap_Entry) {
         $dn = $dn->dn();
     }
     if (!is_string($dn)) {
         throw new Horde_Ldap_Exception('Parameter $dn is not a string nor an entry object!');
     }
     /* Make dn relative to parent. */
     $options = array('casefold' => 'none');
     $base = Horde_Ldap_Util::explodeDN($dn, $options);
     $entry_rdn = '(&(' . Horde_Ldap_Util::canonicalDN(array_shift($base), array_merge($options, array('separator' => ')('))) . '))';
     $base = Horde_Ldap_Util::canonicalDN($base, $options);
     $result = @ldap_list($this->_link, $base, $entry_rdn, array('dn'), 1, 1);
     if ($result && @ldap_count_entries($this->_link, $result)) {
         return true;
     }
     if ($this->errorName(@ldap_errno($this->_link)) == 'LDAP_NO_SUCH_OBJECT') {
         return false;
     }
     if (@ldap_errno($this->_link)) {
         throw new Horde_Ldap_Exception(@ldap_error($this->_link), @ldap_errno($this->_link));
     }
     return false;
 }
Esempio n. 7
0
 /**
  * Creates a new part of an LDAP filter.
  *
  * The following matching rules exists:
  * - equals:         One of the attributes values is exactly $value.
  *                   Please note that case sensitiviness depends on the
  *                   attributes syntax configured in the server.
  * - begins:         One of the attributes values must begin with $value.
  * - ends:           One of the attributes values must end with $value.
  * - contains:       One of the attributes values must contain $value.
  * - present | any:  The attribute can contain any value but must exist.
  * - greater:        The attributes value is greater than $value.
  * - less:           The attributes value is less than $value.
  * - greaterOrEqual: The attributes value is greater or equal than $value.
  * - lessOrEqual:    The attributes value is less or equal than $value.
  * - approx:         One of the attributes values is similar to $value.
  *
  * If $escape is set to true then $value will be escaped. If set to false
  * then $value will be treaten as a raw filter value string.  You should
  * then escape it yourself using {@link
  * Horde_Ldap_Util::escapeFilterValue()}.
  *
  * Examples:
  * <code>
  * // This will find entries that contain an attribute "sn" that ends with
  * // "foobar":
  * $filter = Horde_Ldap_Filter::create('sn', 'ends', 'foobar');
  *
  * // This will find entries that contain an attribute "sn" that has any
  * // value set:
  * $filter = Horde_Ldap_Filter::create('sn', 'any');
  * </code>
  *
  * @param string  $attribute Name of the attribute the filter should apply
  *                           to.
  * @param string  $match     Matching rule (equals, begins, ends, contains,
  *                           greater, less, greaterOrEqual, lessOrEqual,
  *                           approx, any).
  * @param string  $value     If given, then this is used as a filter value.
  * @param boolean $escape    Should $value be escaped?
  *
  * @return Horde_Ldap_Filter
  * @throws Horde_Ldap_Exception
  */
 public static function create($attribute, $match, $value = '', $escape = true)
 {
     if ($escape) {
         $array = Horde_Ldap_Util::escapeFilterValue(array($value));
         $value = $array[0];
     }
     switch (Horde_String::lower($match)) {
         case 'equals':
         case '=':
             $filter = '(' . $attribute . '=' . $value . ')';
             break;
         case 'begins':
             $filter = '(' . $attribute . '=' . $value . '*)';
             break;
         case 'ends':
             $filter = '(' . $attribute . '=*' . $value . ')';
             break;
         case 'contains':
             $filter = '(' . $attribute . '=*' . $value . '*)';
             break;
         case 'greater':
         case '>':
             $filter = '(' . $attribute . '>' . $value . ')';
             break;
         case 'less':
         case '<':
             $filter = '(' . $attribute . '<' . $value . ')';
             break;
         case 'greaterorequal':
         case '>=':
             $filter = '(' . $attribute . '>=' . $value . ')';
             break;
         case 'lessorequal':
         case '<=':
             $filter = '(' . $attribute . '<=' . $value . ')';
             break;
         case 'approx':
         case '~=':
             $filter = '(' . $attribute . '~=' . $value . ')';
             break;
         case 'any':
         case 'present':
             $filter = '(' . $attribute . '=*)';
             break;
         default:
             throw new Horde_Ldap_Exception('Matching rule "' . $match . '" unknown');
     }
     return new Horde_Ldap_Filter(array('filter' => $filter));
 }
Esempio n. 8
0
 /**
  * Tests if canonicalDN() works.
  *
  * Note: This tests depend on the default options of canonicalDN().
  */
 public function testCanonicalDN()
 {
     // Test empty dn (is valid according to RFC).
     $this->assertEquals('', Horde_Ldap_Util::canonicalDN(''));
     // Default options with common DN.
     $testdn = 'cn=beni,DC=php,c=net';
     $expected = 'CN=beni,DC=php,C=net';
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn));
     // Casefold tests with common DN.
     $expected_up = 'CN=beni,DC=php,C=net';
     $expected_lo = 'cn=beni,dc=php,c=net';
     $expected_no = 'cn=beni,DC=php,c=net';
     $this->assertEquals($expected_up, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'upper')));
     $this->assertEquals($expected_lo, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'lower')));
     $this->assertEquals($expected_no, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'none')));
     // Reverse.
     $expected_rev = 'C=net,DC=php,CN=beni';
     $this->assertEquals($expected_rev, Horde_Ldap_Util::canonicalDN($testdn, array('reverse' => true)), 'Option reverse failed');
     // DN as arrays.
     $dn_index = array('cn=beni', 'dc=php', 'c=net');
     $dn_assoc = array('cn' => 'beni', 'dc' => 'php', 'c' => 'net');
     $expected = 'CN=beni,DC=php,C=net';
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($dn_index));
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($dn_assoc));
     // DN with multiple RDN value.
     $testdn = 'ou=dev+cn=beni,DC=php,c=net';
     $testdn_index = array(array('ou=dev', 'cn=beni'), 'DC=php', 'c=net');
     $testdn_assoc = array(array('ou' => 'dev', 'cn' => 'beni'), 'DC' => 'php', 'c' => 'net');
     $expected = 'CN=beni+OU=dev,DC=php,C=net';
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn));
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn_assoc));
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($expected));
     // Test DN with OID.
     $testdn = 'OID.2.5.4.3=beni,dc=php,c=net';
     $expected = '2.5.4.3=beni,DC=php,C=net';
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn));
     // Test with leading and ending spaces.
     $testdn = 'cn=  beni  ,DC=php,c=net';
     $expected = 'CN=\\20\\20beni\\20\\20,DC=php,C=net';
     $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn));
     // Test with escaped commas. Doesn't work at the moment because
     // canonicalDN() escapes attribute values, which break if they are
     // already escaped.
     $testdn = 'cn=beni\\,hi\\=ll,DC=php,c=net';
     $expected = 'CN=beni\\,hi\\=ll,DC=php,C=net';
     // $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn));
     // Test with to-be escaped characters in attribute value.
     $specialchars = array(',' => '\\,', '+' => '\\+', '"' => '\\"', '\\' => '\\\\', '<' => '\\<', '>' => '\\>', ';' => '\\;', '#' => '\\#', '=' => '\\=', chr(18) => '\\12', '/' => '\\/');
     foreach ($specialchars as $char => $escape) {
         $test_string = 'CN=be' . $char . 'ni,DC=ph' . $char . 'p,C=net';
         $test_index = array('CN=be' . $char . 'ni', 'DC=ph' . $char . 'p', 'C=net');
         $test_assoc = array('CN' => 'be' . $char . 'ni', 'DC' => 'ph' . $char . 'p', 'C' => 'net');
         $expected = 'CN=be' . $escape . 'ni,DC=ph' . $escape . 'p,C=net';
         $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_string), 'String escaping test (' . $char . ') failed');
         $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_index), 'Indexed array escaping test (' . $char . ') failed');
         $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_assoc), 'Associative array encoding test (' . $char . ') failed');
     }
 }
Esempio n. 9
0
 /**
  * Writes a DN to the file handle.
  *
  * @param string $dn DN to write.
  *
  * @throws Horde_Ldap_Exception
  */
 protected function _writeDN($dn)
 {
     // Prepare DN.
     if ($this->_options['encode'] == 'base64') {
         $dn = $this->_convertDN($dn);
     } elseif ($this->_options['encode'] == 'canonical') {
         $dn = Horde_Ldap_Util::canonicalDN($dn, array('casefold' => 'none'));
     }
     $this->_writeLine($dn, 'Unable to write DN of entry ' . $this->_entrynum);
 }
Esempio n. 10
0
 /**
  * Parse LDAP filter.
  * Partially derived from Net_LDAP_Filter.
  *
  * @param string $filter The filter string.
  *
  * @return array An array of the parsed filter.
  *
  * @throws Horde_Kolab_Server_Exception If parsing the filter expression
  *                                      fails.
  */
 public function parse($filter)
 {
     $result = array();
     if (preg_match('/^\\((.+?)\\)$/', $filter, $matches)) {
         if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
             $result['op'] = substr($matches[1], 0, 1);
             $result['sub'] = $this->parseSub(substr($matches[1], 1));
             return $result;
         } else {
             if (stristr($matches[1], ')(')) {
                 throw new Horde_Kolab_Server_Exception('Filter parsing error: invalid filter syntax - multiple leaf components detected!');
             } else {
                 $filter_parts = preg_split('/(?<!\\\\)(=|=~|>|<|>=|<=)/', $matches[1], 2, PREG_SPLIT_DELIM_CAPTURE);
                 if (count($filter_parts) != 3) {
                     throw new Horde_Kolab_Server_Exception('Filter parsing error: invalid filter syntax - unknown matching rule used');
                 } else {
                     $result['att'] = $filter_parts[0];
                     $result['log'] = $filter_parts[1];
                     $val = Horde_Ldap_Util::unescapeFilterValue($filter_parts[2]);
                     $result['val'] = $val[0];
                     return $result;
                 }
             }
         }
     } else {
         throw new Horde_Kolab_Server_Exception(sprintf("Filter parsing error: %s - filter components must be enclosed in round brackets", $filter));
     }
 }
Esempio n. 11
0
 /**
  * Test adding a person with two common names.
  *
  * @return NULL
  */
 public function testAddDoubleCnPerson()
 {
     foreach ($this->servers as $server) {
         $person = $this->assertAdd($server, $this->objects[5], array());
         $cn_result = $server->uidForCn($this->objects[5]['Cn'][0]);
         $this->assertNoError($cn_result);
         $dn_parts = Horde_Ldap_Util::explodeDN($cn_result, array('casefold' => 'lower'));
         $dnpart = Horde_Ldap_Util::unescapeDNValue($dn_parts[0]);
         $this->assertContains('Cn' . '=' . $this->objects[5]['Cn'][0], $dnpart[0]);
     }
 }
Esempio n. 12
0
 /**
  * Returns a list of users in a group.
  *
  * @param mixed $gid  A group ID.
  *
  * @return array  List of group users.
  * @throws Horde_Group_Exception
  * @throws Horde_Exception_NotFound
  */
 public function listUsers($gid)
 {
     $attr = $this->_params['memberuid'];
     try {
         $entry = $this->_ldap->getEntry($gid, array($attr));
         if (!$entry->exists($attr)) {
             return array();
         }
         if (empty($this->_params['attrisdn'])) {
             return $entry->getValue($attr, 'all');
         }
         $users = array();
         foreach ($entry->getValue($attr, 'all') as $user) {
             $dn = Horde_Ldap_Util::explodeDN($user, array('onlyvalues' => true));
             // Very simplified approach: assume the first element of the DN
             // contains the user ID.
             $user = $dn[0];
             // Check for multi-value RDNs.
             if (is_array($element)) {
                 $user = $element[0];
             }
             $users[] = $user;
         }
         return $users;
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Group_Exception($e);
     }
 }