The purpose of this class is to easily build LDAP filters without having to worry about correct escaping etc. A filter is built using several independent filter objects which are combined afterwards. This object works in two modes, depending how the object is created. If the object is created using the {@link create()} method, then this is a leaf-object. If the object is created using the {@link combine()} method, then this is a container object. LDAP filters are defined in RFC 2254.
See also: http://www.ietf.org/rfc/rfc2254.txt A short example: $filter0 = Horde_Ldap_Filter::create('stars', 'equals', '***'); $filter_not0 = Horde_Ldap_Filter::combine('not', $filter0); $filter1 = Horde_Ldap_Filter::create('gn', 'begins', 'bar'); $filter2 = Horde_Ldap_Filter::create('gn', 'ends', 'baz'); $filter_comp = Horde_Ldap_Filter::combine('or', array($filter_not0, $filter1, $filter2)); echo (string)$filter_comp; // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz)) // The stars in $filter0 are treaten as real stars unless you disable escaping. 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)
Example #1
0
 /**
  * Retrieves user preferences from the backend.
  *
  * @throws Sam_Exception
  */
 public function retrieve()
 {
     $attrib = Horde_String::lower($this->_params['attribute']);
     try {
         $search = $this->_ldap->search($this->_params['basedn'], Horde_Ldap_Filter::create($this->_params['uid'], 'equals', $this->_user), array('attributes' => array($attrib)));
         $entry = $search->shiftEntry();
         if (!$entry) {
             throw new Sam_Exception(sprintf('LDAP user "%s" not found.', $this->_user));
         }
         foreach ($entry->getValue($attrib, 'all') as $attribute) {
             list($a, $v) = explode(' ', $attribute);
             $ra = $this->_mapOptionToAttribute($a);
             if (is_numeric($v)) {
                 if (strstr($v, '.')) {
                     $newoptions[$ra][] = (double) $v;
                 } else {
                     $newoptions[$ra][] = (int) $v;
                 }
             } else {
                 $newoptions[$ra][] = $v;
             }
         }
     } catch (Horde_Ldap_Exception $e) {
         throw new Sam_Exception($e);
     }
     /* Go through new options and pull single values out of their
      * arrays. */
     foreach ($newoptions as $k => $v) {
         if (count($v) > 1) {
             $this->_options[$k] = $v;
         } else {
             $this->_options[$k] = $v[0];
         }
     }
 }
Example #2
0
 /**
  * Tries to find a DN for a given kolab mail address.
  *
  * @param string $mail  The mail address to search for.
  *
  * @return string  The corresponding dn or false.
  * @throws Horde_Group_Exception
  */
 protected function _dnForMail($mail)
 {
     try {
         $filter = Horde_Ldap_Filter::combine('and', array(Horde_Ldap_Filter::create('objectclass', 'equals', 'kolabInetOrgPerson'), Horde_Ldap_Filter::create('mail', 'equals', $mail)));
         $search = $this->_ldap->search($this->_params['basedn'], $filter, array('dn'));
         if ($search->count()) {
             return $search->shiftEntry()->dn();
         }
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Group_Exception($e);
     }
     throw new Horde_Group_Exception(sprintf('Error searching for user with the email address "%s"', $mail));
 }
Example #3
0
 /**
  * Checks if $userId exists in the LDAP backend system.
  *
  * @author Marco Ferrante, University of Genova (I)
  *
  * @param string $userId  User ID for which to check
  *
  * @return boolean  Whether or not $userId already exists.
  */
 public function exists($userId)
 {
     $params = array('scope' => $this->_params['scope']);
     try {
         $uidfilter = Horde_Ldap_Filter::create($this->_params['uid'], 'equals', $userId);
         $classfilter = Horde_Ldap_Filter::build(array('filter' => $this->_params['filter']));
         $search = $this->_ldap->search($this->_params['basedn'], Horde_Ldap_Filter::combine('and', array($uidfilter, $classfilter)), $params);
         if ($search->count() < 1) {
             return false;
         }
         if ($search->count() > 1 && $this->_logger) {
             $this->_logger->log('Multiple LDAP entries with user identifier ' . $userId, 'WARN');
         }
         return true;
     } catch (Horde_Ldap_Exception $e) {
         if ($this->_logger) {
             $this->_logger->log('Error searching LDAP user: '******'ERR');
         }
         return false;
     }
 }
Example #4
0
 /**
  * Modifies the specified entry in the LDAP directory.
  *
  * @param Turba_Object $object  The object we wish to save.
  *
  * @return string  The object id, possibly updated.
  * @throw Turba_Exception
  */
 protected function _save(Turba_Object $object)
 {
     $this->_connect();
     list($object_key, $object_id) = each($this->toDriverKeys(array('__key' => $object->getValue('__key'))));
     $attributes = $this->toDriverKeys($object->getAttributes());
     /* Get the old entry so that we can access the old
      * values. These are needed so that we can delete any
      * attributes that have been removed by using ldap_mod_del. */
     if (empty($this->_params['objectclass'])) {
         $filter = null;
     } else {
         $filter = (string) Horde_Ldap_Filter::build(array('objectclass' => $this->_params['objectclass']), 'or');
     }
     $oldres = @ldap_read($this->_ds, Horde_String::convertCharset($object_id, 'UTF-8', $this->_params['charset']), $filter, array_merge(array_keys($attributes), array('objectclass')));
     $info = ldap_get_attributes($this->_ds, ldap_first_entry($this->_ds, $oldres));
     if ($this->_params['version'] == 3 && Horde_String::lower(str_replace(array(',', '"'), array('\\2C', ''), $this->_makeKey($attributes))) != Horde_String::lower(str_replace(',', '\\2C', $object_id))) {
         /* Need to rename the object. */
         $newrdn = $this->_makeRDN($attributes);
         if ($newrdn == '') {
             throw new Turba_Exception(_("Missing DN in LDAP source configuration."));
         }
         if (ldap_rename($this->_ds, Horde_String::convertCharset($object_id, 'UTF-8', $this->_params['charset']), Horde_String::convertCharset($newrdn, 'UTF-8', $this->_params['charset']), $this->_params['root'], true)) {
             $object_id = $newrdn . ',' . $this->_params['root'];
         } else {
             throw new Turba_Exception(sprintf(_("Failed to change name: (%s) %s; Old DN = %s, New DN = %s, Root = %s"), ldap_errno($this->_ds), ldap_error($this->_ds), $object_id, $newrdn, $this->_params['root']));
         }
     }
     /* Work only with lowercase keys. */
     $info = array_change_key_case($info, CASE_LOWER);
     $attributes = array_change_key_case($attributes, CASE_LOWER);
     foreach ($info as $key => $var) {
         $oldval = null;
         /* Check to see if the old value and the new value are
          * different and that the new value is empty. If so then
          * we use ldap_mod_del to delete the attribute. */
         if (isset($attributes[$key]) && $var[0] != $attributes[$key] && $attributes[$key] == '') {
             $oldval[$key] = $var[0];
             if (!@ldap_mod_del($this->_ds, Horde_String::convertCharset($object_id, 'UTF-8', $this->_params['charset']), $oldval)) {
                 throw new Turba_Exception(sprintf(_("Modify failed: (%s) %s"), ldap_errno($this->_ds), ldap_error($this->_ds)));
             }
             unset($attributes[$key]);
         } elseif (isset($attributes[$key]) && $var[0] == $attributes[$key]) {
             /* Drop unchanged elements from list of attributes to write. */
             unset($attributes[$key]);
         }
     }
     unset($attributes[Horde_String::lower($object_key)]);
     $this->_encodeAttributes($attributes);
     $attributes = array_filter($attributes, array($this, '_emptyAttributeFilter'));
     /* Modify objectclasses only if they really changed. */
     $oldClasses = array_map(array('Horde_String', 'lower'), $info['objectclass']);
     array_shift($oldClasses);
     $attributes['objectclass'] = array_unique(array_map('strtolower', array_merge($info['objectclass'], $this->_params['objectclass'])));
     unset($attributes['objectclass']['count']);
     $attributes['objectclass'] = array_values($attributes['objectclass']);
     /* Do not handle object classes unless they have changed. */
     if (!array_diff($oldClasses, $attributes['objectclass'])) {
         unset($attributes['objectclass']);
     }
     if (!@ldap_modify($this->_ds, Horde_String::convertCharset($object_id, 'UTF-8', $this->_params['charset']), $attributes)) {
         throw new Turba_Exception(sprintf(_("Modify failed: (%s) %s"), ldap_errno($this->_ds), ldap_error($this->_ds)));
     }
     return $object_id;
 }
Example #5
0
 /**
  * Returns the DN of a user.
  *
  * The purpose is to quickly find the full DN of a user so it can be used
  * to re-bind as this user. This method requires the 'user' configuration
  * parameter to be set.
  *
  * @param string $user  The user to find.
  *
  * @return string  The user's full DN.
  * @throws Horde_Ldap_Exception
  * @throws Horde_Exception_NotFound
  */
 public function findUserDN($user)
 {
     $filter = Horde_Ldap_Filter::combine('and', array(Horde_Ldap_Filter::build($this->_config['user']), Horde_Ldap_Filter::create($this->_config['user']['uid'], 'equals', $user)));
     $search = $this->search(isset($this->_config['user']['basedn']) ? $this->_config['user']['basedn'] : null, $filter, array('attributes' => array($this->_config['user']['uid'])));
     if (!$search->count()) {
         throw new Horde_Exception_NotFound('DN for user ' . $user . ' not found');
     }
     $entry = $search->shiftEntry();
     return $entry->currentDN();
 }
Example #6
0
 /**
  * Convert the group element to query format.
  *
  * @param Horde_Kolab_Server_Query_Element_Group $group    The element to convert.
  * @param string                                 $operator The element operation.
  *
  * @return mixed The query element in query format.
  *
  * @throws Horde_Kolab_Server_Exception If the query is malformed.
  */
 public function _convertGroup(Horde_Kolab_Server_Query_Element_Group $group, $operator)
 {
     $filters = array();
     foreach ($group->getElements() as $element) {
         $filters[] = $element->convert($this);
     }
     try {
         return Horde_Ldap_Filter::combine($operator, $filters);
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Kolab_Server_Exception($e->getMessage(), Horde_Kolab_Server_Exception::INVALID_QUERY, $e);
     }
 }
Example #7
0
 /**
  * This tests the basic combination of filters.
  */
 public function testCombine()
 {
     // Setup.
     $filter0 = Horde_Ldap_Filter::create('foo', 'equals', 'bar');
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter0);
     $filter1 = Horde_Ldap_Filter::create('bar', 'equals', 'foo');
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter1);
     $filter2 = Horde_Ldap_Filter::create('you', 'equals', 'me');
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter2);
     $filter3 = Horde_Ldap_Filter::parse('(perlinterface=used)');
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter3);
     // Negation test.
     $filter_not1 = Horde_Ldap_Filter::combine('not', $filter0);
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_not1, 'Negation failed for literal NOT');
     $this->assertEquals('(!(foo=bar))', (string) $filter_not1);
     $filter_not2 = Horde_Ldap_Filter::combine('!', $filter0);
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_not2, 'Negation failed for logical NOT');
     $this->assertEquals('(!(foo=bar))', (string) $filter_not2);
     $filter_not3 = Horde_Ldap_Filter::combine('!', (string) $filter0);
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_not3, 'Negation failed for logical NOT');
     $this->assertEquals('(!' . $filter0 . ')', (string) $filter_not3);
     // Combination test: OR
     $filter_comb_or1 = Horde_Ldap_Filter::combine('or', array($filter1, $filter2));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_or1, 'Combination failed for literal OR');
     $this->assertEquals('(|(bar=foo)(you=me))', (string) $filter_comb_or1);
     $filter_comb_or2 = Horde_Ldap_Filter::combine('|', array($filter1, $filter2));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_or2, 'combination failed for logical OR');
     $this->assertEquals('(|(bar=foo)(you=me))', (string) $filter_comb_or2);
     // Combination test: AND
     $filter_comb_and1 = Horde_Ldap_Filter::combine('and', array($filter1, $filter2));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_and1, 'Combination failed for literal AND');
     $this->assertEquals('(&(bar=foo)(you=me))', (string) $filter_comb_and1);
     $filter_comb_and2 = Horde_Ldap_Filter::combine('&', array($filter1, $filter2));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_and2, 'combination failed for logical AND');
     $this->assertEquals('(&(bar=foo)(you=me))', (string) $filter_comb_and2);
     // Combination test: using filter created with perl interface.
     $filter_comb_perl1 = Horde_Ldap_Filter::combine('and', array($filter1, $filter3));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_perl1, 'Combination failed for literal AND');
     $this->assertEquals('(&(bar=foo)(perlinterface=used))', (string) $filter_comb_perl1);
     $filter_comb_perl2 = Horde_Ldap_Filter::combine('&', array($filter1, $filter3));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_perl2, 'combination failed for logical AND');
     $this->assertEquals('(&(bar=foo)(perlinterface=used))', (string) $filter_comb_perl2);
     // Combination test: using filter_str instead of object
     $filter_comb_fstr1 = Horde_Ldap_Filter::combine('and', array($filter1, '(filter_str=foo)'));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comb_fstr1, 'Combination failed for literal AND using filter_str');
     $this->assertEquals('(&(bar=foo)(filter_str=foo))', (string) $filter_comb_fstr1);
     // Combination test: deep combination
     $filter_comp_deep = Horde_Ldap_Filter::combine('and', array($filter2, $filter_not1, $filter_comb_or1, $filter_comb_perl1));
     $this->assertInstanceOf('Horde_Ldap_Filter', $filter_comp_deep, 'Deep combination failed!');
     $this->assertEquals('(&(you=me)(!(foo=bar))(|(bar=foo)(you=me))(&(bar=foo)(perlinterface=used)))', (string) $filter_comp_deep);
     // Test failure in combination
     try {
         Horde_Ldap_Filter::create('foo', 'test_undefined_matchingrule', 'bar');
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('not', 'damaged_filter_str');
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('not', array($filter0, $filter1));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('not', null);
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('and', $filter_not1);
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('and', array($filter_not1));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('and', $filter_not1);
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('or', array($filter_not1));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('some_unknown_method', array($filter_not1));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('and', array($filter_not1, 'some_invalid_filterstring'));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     try {
         Horde_Ldap_Filter::combine('and', array($filter_not1, null));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
 }
Example #8
0
 /**
  * Test search().
  */
 public function testSearch()
 {
     $ldap = new Horde_Ldap(self::$ldapcfg['server']);
     // Some testdata, so we can test sizelimit.
     $base = self::$ldapcfg['server']['basedn'];
     $ou1 = Horde_Ldap_Entry::createFresh('ou=Horde_Ldap_Test_search1,' . $base, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'Horde_Ldap_Test_search1'));
     $ou1_1 = Horde_Ldap_Entry::createFresh('ou=Horde_Ldap_Test_search1_1,' . $ou1->dn(), array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'Horde_Ldap_Test_search1_1'));
     $ou2 = Horde_Ldap_Entry::createFresh('ou=Horde_Ldap_Test_search2,' . $base, array('objectClass' => array('top', 'organizationalUnit'), 'ou' => 'Horde_Ldap_Test_search2'));
     $ldap->add($ou1);
     $this->assertTrue($ldap->exists($ou1->dn()));
     $ldap->add($ou1_1);
     $this->assertTrue($ldap->exists($ou1_1->dn()));
     $ldap->add($ou2);
     $this->assertTrue($ldap->exists($ou2->dn()));
     // Search for test filter, should at least return our two test entries.
     $res = $ldap->search(null, '(ou=Horde_Ldap*)', array('attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertThat($res->count(), $this->greaterThanOrEqual(2));
     // Same, but with Horde_Ldap_Filter object.
     $filtero = Horde_Ldap_Filter::create('ou', 'begins', 'Horde_Ldap');
     $this->assertInstanceOf('Horde_Ldap_Filter', $filtero);
     $res = $ldap->search(null, $filtero, array('attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertThat($res->count(), $this->greaterThanOrEqual(2));
     // Search using default filter for base-onelevel scope, should at least
     // return our two test entries.
     $res = $ldap->search(null, null, array('scope' => 'one', 'attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertThat($res->count(), $this->greaterThanOrEqual(2));
     // Base-search using custom base (string), should only return the test
     // entry $ou1 and not the entry below it.
     $res = $ldap->search($ou1->dn(), null, array('scope' => 'base', 'attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertEquals(1, $res->count());
     // Search using custom base, this time using an entry object.  This
     // tests if passing an entry object as base works, should only return
     // the test entry $ou1.
     $res = $ldap->search($ou1, '(ou=*)', array('scope' => 'base', 'attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertEquals(1, $res->count());
     // Search using default filter for base-onelevel scope with sizelimit,
     // should of course return more than one entry, but not more than
     // sizelimit
     $res = $ldap->search(null, null, array('scope' => 'one', 'sizelimit' => 1, 'attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertEquals(1, $res->count());
     // Sizelimit should be exceeded now.
     $this->assertTrue($res->sizeLimitExceeded());
     // Bad filter.
     try {
         $res = $ldap->search(null, 'somebadfilter', array('attributes' => '1.1'));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     // Bad base.
     try {
         $res = $ldap->search('badbase', null, array('attributes' => '1.1'));
         $this->fail('Horde_Ldap_Exception expected.');
     } catch (Horde_Ldap_Exception $e) {
     }
     // Nullresult.
     $res = $ldap->search(null, '(cn=nevermatching_filter)', array('scope' => 'base', 'attributes' => '1.1'));
     $this->assertInstanceOf('Horde_Ldap_Search', $res);
     $this->assertEquals(0, $res->count());
 }
Example #9
0
 /**
  * Lists all available scopes.
  *
  * @return array The list of scopes stored in the backend.
  */
 public function listScopes()
 {
     $scopes = array();
     try {
         $prefs = $this->_ldap->search($this->_prefsDN, Horde_Ldap_Filter::create('objectclass', 'equals', 'hordePerson'), array('attributes' => array('@hordePerson'), 'scope' => 'base', 'attrsonly' => true));
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Prefs_Exception($e);
     }
     if (!$prefs) {
         return $scopes;
     }
     foreach ($prefs->shiftEntry()->attributes() as $attr) {
         // Trim off prefs from attribute name to get scope (e.g. hordePrefs
         // -> horde).
         $scope = str_ireplace("prefs", "", $attr);
         // Skip non-prefs attributes like objectclass (no replacement
         // occurred above).
         if ($attr != $scope) {
             $scopes[] = $scope;
         }
     }
     return $scopes;
 }
Example #10
0
 /**
  * Searches for group names.
  *
  * @param string $name  A search string.
  *
  * @return array  A list of matching groups, with IDs as keys and names as
  *                values.
  * @throws Horde_Group_Exception
  */
 public function search($name)
 {
     $attr = $this->_params['gid'];
     try {
         $result = $this->_ldap->search($this->_params['basedn'], Horde_Ldap_Filter::create($attr, 'contains', $name), array($attr));
     } catch (Horde_Ldap_Exception $e) {
         throw new Horde_Group_Exception($e);
     }
     $entries = array();
     foreach ($result->sortedAsArray(array($attr)) as $entry) {
         $entries[$entry['dn']] = $entry[$attr][0];
     }
     return $entries;
 }