lib/ext

Aleksander Machniak machniak at kolabsys.com
Thu Feb 26 12:18:05 CET 2015


 lib/ext/Net/LDAP3.php        |  353 +++++++++++++++++++++++++++++++++----------
 lib/ext/Net/LDAP3/Result.php |   15 +
 2 files changed, 290 insertions(+), 78 deletions(-)

New commits:
commit b417fb2ef03ce75162b79170e21a05117cc6cf92
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Thu Feb 26 12:17:40 2015 +0100

    Update built-in Net/LDAP3 package

diff --git a/lib/ext/Net/LDAP3.php b/lib/ext/Net/LDAP3.php
index 7e463f0..37a0ea3 100644
--- a/lib/ext/Net/LDAP3.php
+++ b/lib/ext/Net/LDAP3.php
@@ -8,9 +8,18 @@
  | Copyright (C) 2006-2014, The Roundcube Dev Team                       |
  | Copyright (C) 2012-2014, Kolab Systems AG                             |
  |                                                                       |
- | Licensed under the GNU General Public License version 3 or            |
- | any later version with exceptions for plugins.                        |
- | See the README file for a full license statement.                     |
+ | This program is free software: you can redistribute it and/or modify  |
+ | it under the terms of the GNU General Public License as published by  |
+ | the Free Software Foundation, either version 3 of the License, or     |
+ | (at your option) any later version.                                   |
+ |                                                                       |
+ | This program is distributed in the hope that it will be useful,       |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of        |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          |
+ | GNU General Public License for more details.                          |
+ |                                                                       |
+ | You should have received a copy of the GNU General Public License     |
+ | along with this program.  If not, see <http://www.gnu.org/licenses/>. |
  |                                                                       |
  | PURPOSE:                                                              |
  |   Provide advanced functionality for accessing LDAP directories       |
@@ -689,15 +698,16 @@ class Net_LDAP3
             'entryLevelRights' => array(),
         );
 
-        $output   = array();
         $entry_dn = $this->entry_dn($subject);
 
         if (!$entry_dn) {
             $entry_dn = $this->config_get($subject . "_base_dn");
         }
+
         if (!$entry_dn) {
             $entry_dn = $this->config_get("base_dn");
         }
+
         if (!$entry_dn) {
             $entry_dn = $this->config_get("root_dn");
         }
@@ -717,34 +727,68 @@ class Net_LDAP3
             return null;
         }
 
-        $command = array(
-            $moz_ldapsearch,
-            '-x',
-            '-h',
-            $this->_ldap_server,
-            '-p',
-            $this->_ldap_port,
-            '-b',
-            escapeshellarg($entry_dn),
-            '-D',
-            escapeshellarg($this->_current_bind_dn),
-            '-w',
-            escapeshellarg($this->_current_bind_pw),
-            '-J',
-            escapeshellarg(implode(':', array(
-                $effective_rights_control_oid,          // OID
-                'true',                                 // Criticality
-                'dn:' . $this->_current_bind_dn // User DN
-            ))),
-            '-s',
-            'base',
-            '"(objectclass=*)"',
-            '"*"',
-        );
+        $output = array();
+        $command = Array(
+                $moz_ldapsearch,
+                '-x',
+                '-h',
+                $this->_current_host,
+                '-p',
+                $this->config_get('port', 389),
+                '-b',
+                escapeshellarg($entry_dn),
+                '-s',
+                'base',
+                '-D',
+                escapeshellarg($this->_current_bind_dn),
+                '-w',
+                escapeshellarg($this->_current_bind_pw)
+            );
+
+        if ($this->vendor_name() == "Oracle Corporation") {
+            // For Oracle DSEE
+            $command[] = "-J";
+            $command[] = escapeshellarg(
+                    implode(
+                            ':',
+                            Array(
+                                    $effective_rights_control_oid,          // OID
+                                    'true'                                  // Criticality
+                                )
+                        )
+                );
+            $command[] = "-c";
+            $command[] = escapeshellarg(
+                    'dn:' . $this->_current_bind_dn
+                );
+
+        } else {
+            // For 389 DS:
+            $command[] = "-J";
+            $command[] = escapeshellarg(
+                    implode(
+                            ':',
+                            Array(
+                                    $effective_rights_control_oid,          // OID
+                                    'true',                                 // Criticality
+                                    'dn:' . $this->_current_bind_dn         // User DN
+                                )
+                        )
+                );
+        }
+
+        // For both
+        $command[] = '"(objectclass=*)"';
+        $command[] = '"*"';
+
+        if ($this->vendor_name() == "Oracle Corporation") {
+            // Oracle DSEE
+            $command[] = 'aclRights';
+        }
 
         // remove password from debug log
         $command_debug     = $command;
-        $command_debug[11] = '*';
+        $command_debug[13] = '*';
 
         $command       = implode(' ', $command);
         $command_debug = implode(' ', $command_debug);
@@ -771,24 +815,46 @@ class Net_LDAP3
             }
         }
 
-        foreach ($lines as $line) {
-            $line_components = explode(':', $line);
-            $attribute_name  = array_shift($line_components);
-            $attribute_value = trim(implode(':', $line_components));
-
-            switch ($attribute_name) {
-                case "attributeLevelRights":
-                    $attributes[$attribute_name] = $this->parse_attribute_level_rights($attribute_value);
-                    break;
-                case "dn":
-                    $attributes[$attribute_name] = $attribute_value;
-                    break;
-                case "entryLevelRights":
-                    $attributes[$attribute_name] = $this->parse_entry_level_rights($attribute_value);
-                    break;
+        if ($this->vendor_name() == "Oracle Corporation") {
+            // Example for attribute level rights:
+            // aclRights;attributeLevel;$attr:$right:$bool,$right:$bool
+            // Example for entry level rights:
+            // aclRights;entryLevel: add:1,delete:1,read:1,write:1,proxy:1
+            foreach ($lines as $line) {
+                $line_components = explode(':', $line);
+                $attribute_name = explode(';', array_shift($line_components));
+
+                switch ($attribute_name[0]) {
+                    case "aclRights":
+                        $this->parse_aclrights($attributes, $line);
+                        break;
+                    case "dn":
+                        $attributes[$attribute_name[0]] = trim(implode(';', $line_components));
+                        break;
+                    default:
+                        break;
+                }
+            }
 
-                default:
-                    break;
+        } else {
+            foreach ($lines as $line) {
+                $line_components = explode(':', $line);
+                $attribute_name  = array_shift($line_components);
+                $attribute_value = trim(implode(':', $line_components));
+
+                switch ($attribute_name) {
+                    case "attributeLevelRights":
+                        $attributes[$attribute_name] = $this->parse_attribute_level_rights($attribute_value);
+                        break;
+                    case "dn":
+                        $attributes[$attribute_name] = $attribute_value;
+                        break;
+                    case "entryLevelRights":
+                        $attributes[$attribute_name] = $this->parse_entry_level_rights($attribute_value);
+                        break;
+                    default:
+                        break;
+                }
             }
         }
 
@@ -1192,8 +1258,8 @@ class Net_LDAP3
 
         // This is me cheating. Remove this special attribute.
         if (array_key_exists('ou', $old_attrs) || array_key_exists('ou', $new_attrs)) {
-            $old_ou = $old_attrs['ou'];
-            $new_ou = $new_attrs['ou'];
+            $old_ou = is_array($old_attrs['ou']) ? array_shift($old_attrs['ou']) : $old_attrs['ou'];
+            $new_ou = is_array($new_attrs['ou']) ? array_shift($new_attrs['ou']) : $new_attrs['ou'];
             unset($old_attrs['ou']);
             unset($new_attrs['ou']);
         }
@@ -1495,12 +1561,18 @@ class Net_LDAP3
             return false;
         }
 
-        $this->_debug("C: Search base dn: [$base_dn] scope [$scope] with filter [$filter]");
-
         // make sure attributes list is not empty
         if (empty($attrs)) {
             $attrs = array('dn');
         }
+        // make sure filter is not empty
+        if (empty($filter)) {
+            $filter = '(objectclass=*)';
+        }
+
+        $this->_debug("C: Search base dn: [$base_dn] scope [$scope] with filter [$filter]");
+
+        $function = self::scope_to_function($scope, $ns_function);
 
         if (!$count_only && ($sort = $this->find_vlv($base_dn, $filter, $scope, $props['sort']))) {
             // when using VLV, we get the total count by...
@@ -1516,6 +1588,7 @@ class Net_LDAP3
             }
             // ...or by fetching all records dn and count them
             else if (!function_exists('ldap_parse_virtuallist_control')) {
+                // @FIXME: this search will ignore $props['search']
                 $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $props, true);
             }
 
@@ -1526,9 +1599,15 @@ class Net_LDAP3
             $this->vlv_active = false;
         }
 
-        $function  = self::scope_to_function($scope, $ns_function);
         $sizelimit = (int) $this->config['sizelimit'];
         $timelimit = (int) $this->config['timelimit'];
+        $phplimit  = (int) @ini_get('max_execution_time');
+
+        // set LDAP time limit to be (one second) less than PHP time limit
+        // otherwise we have no chance to log the error below
+        if ($phplimit && $timelimit >= $phplimit) {
+            $timelimit = $phplimit - 1;
+        }
 
         $this->_debug("Using function $function on scope $scope (\$ns_function is $ns_function)");
 
@@ -1553,7 +1632,7 @@ class Net_LDAP3
         $ldap_result = @$function($this->conn, $base_dn, $filter, $attrs, 0, $sizelimit, $timelimit);
 
         if (!$ldap_result) {
-            $this->_debug("$function failed for dn=$base_dn: ".ldap_error($this->conn));
+            $this->_warning("$function failed for dn=$base_dn: ".ldap_error($this->conn));
             return false;
         }
 
@@ -1568,14 +1647,20 @@ class Net_LDAP3
                 $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn)));
             }
         }
-        else if ($this->debug) {
+        else {
             $this->_debug("S: ".ldap_count_entries($this->conn, $ldap_result)." record(s) found");
         }
 
         $result = new Net_LDAP3_Result($this->conn, $base_dn, $filter, $scope, $ldap_result);
-        $result->set('offset', $last_offset);
-        $result->set('count', $vlv_count);
-        $result->set('vlv', true);
+
+        if (isset($last_offset)) {
+            $result->set('offset', $last_offset);
+        }
+        if (isset($vlv_count)) {
+            $result->set('count', $vlv_count);
+        }
+
+        $result->set('vlv', $this->vlv_active);
 
         return $count_only ? $result->count() : $result;
     }
@@ -1636,6 +1721,8 @@ class Net_LDAP3
         $filter    = '';
 
         foreach ((array) $search['params'] as $field => $param) {
+            $value = (array) $param['value'];
+
             switch ((string)$param['type']) {
                 case 'prefix':
                     $prefix = '';
@@ -1654,6 +1741,13 @@ class Net_LDAP3
                 case '<=':
                     $prefix = '';
                     $suffix = '';
+
+                    // this is a common query to find entry by DN, make sure
+                    // it is a unified DN so special characters are handled correctly
+                    if ($field == 'entrydn') {
+                        $value = array_map(array('Net_LDAP3', 'unified_dn'), $value);
+                    }
+
                     break;
 
                 case 'exists':
@@ -1671,16 +1765,20 @@ class Net_LDAP3
 
             $operator = $param['type'] && in_array($param['type'], $operators) ? $param['type'] : '=';
 
-            if (is_array($param['value'])) {
+            if (count($value) < 2) {
+                $value = array_pop($value);
+            }
+
+            if (is_array($value)) {
                 $val_filter = array();
-                foreach ($param['value'] as $val) {
-                    $value = self::quote_string($val);
-                    $val_filter[] = "(" . $field . $operator . $prefix . $value . $suffix . ")";
+                foreach ($value as $val) {
+                    $val          = self::quote_string($val);
+                    $val_filter[] = "(" . $field . $operator . $prefix . $val . $suffix . ")";
                 }
                 $filter .= "(|" . implode($val_filter, '') . ")";
             }
             else {
-                $value = self::quote_string($param['value']);
+                $value = self::quote_string($value);
                 $filter .= "(" . $field . $operator . $prefix . $value . $suffix . ")";
             }
         }
@@ -1861,25 +1959,28 @@ class Net_LDAP3
 
                         // Not passing any sort attributes means you don't care
                         if (!empty($sort_attrs)) {
-                            $sort_attrs = (array) $sort_attrs;
+                            $sort_attrs = array_map('strtolower', (array) $sort_attrs);
+
                             foreach ($vlv_index[$base_dn]['sort'] as $sss_config) {
+                                $sss_config = array_map('strtolower', $sss_config);
                                 if (count(array_intersect($sort_attrs, $sss_config)) == count($sort_attrs)) {
+                                    $this->_debug("Sorting matches");
+
                                     return $sort_attrs;
                                 }
                             }
 
-                            $this->_error("The requested sorting does not match any server-side sorting configuration");
-
-                            return false;
+                            $this->_debug("Sorting does not match");
                         }
                         else {
-                            return $vlv_index[$base_dn]['sort'][0];
+                            $sort = array_filter((array) $vlv_index[$base_dn]['sort'][0]);
+                            $this->_debug("Sorting unimportant");
+
+                            return $sort;
                         }
                     }
                     else {
-                        $this->_debug("Scope does not match. VLV: " . var_export($vlv_index[$base_dn]['scope'], true)
-                            . " while looking for " . var_export($scope, true));
-                        return false;
+                        $this->_debug("Scope does not match");
                     }
                 }
                 else {
@@ -1897,14 +1998,47 @@ class Net_LDAP3
      */
     protected function find_vlv_indexes_and_searches()
     {
+        // Use of Virtual List View control has been specifically disabled.
         if ($this->config['vlv'] === false) {
             return false;
         }
 
+        // Virtual List View control has been configured in kolab.conf, for example;
+        //
+        // [ldap]
+        // vlv = [
+        //         {
+        //                 'ou=People,dc=example,dc=org': {
+        //                         'scope': 'sub',
+        //                         'filter': '(objectclass=inetorgperson)',
+        //                         'sort' : [
+        //                                 [
+        //                                         'displayname',
+        //                                         'sn',
+        //                                         'givenname',
+        //                                         'cn'
+        //                                     ]
+        //                             ]
+        //                     }
+        //             },
+        //         {
+        //                 'ou=Groups,dc=example,dc=org': {
+        //                         'scope': 'sub',
+        //                         'filter': '(objectclass=groupofuniquenames)',
+        //                         'sort' : [
+        //                                 [
+        //                                         'cn'
+        //                                     ]
+        //                             ]
+        //                     }
+        //             },
+        //     ]
+        //
         if (is_array($this->config['vlv'])) {
             return $this->config['vlv'];
         }
 
+        // We have done this dance before.
         if ($this->_vlv_indexes_and_searches !== null) {
             return $this->_vlv_indexes_and_searches;
         }
@@ -1962,7 +2096,7 @@ class Net_LDAP3
             $_vlv_sort = array();
 
             foreach ($vlv_indexes as $vlv_index_dn => $vlv_index_attrs) {
-                $_vlv_sort[] = explode(' ', $vlv_index_attrs['vlvsort']);
+                $_vlv_sort[] = explode(' ', trim($vlv_index_attrs['vlvsort']));
             }
 
             $this->_vlv_indexes_and_searches[] = array(
@@ -2247,6 +2381,45 @@ class Net_LDAP3
         return true;
     }
 
+    private function parse_aclrights(&$attributes, $attribute_value) {
+        $components = explode(':', $attribute_value);
+        $_acl_target = array_shift($components);
+        $_acl_value = trim(implode(':', $components));
+
+        $_acl_components = explode(';', $_acl_target);
+
+        switch ($_acl_components[1]) {
+            case "entryLevel":
+                $attributes['entryLevelRights'] = Array();
+                $_acl_value = explode(',', $_acl_value);
+
+                foreach ($_acl_value as $right) {
+                    list($method, $bool) = explode(':', $right);
+                    if ($bool == "1" && !in_array($method, $attributes['entryLevelRights'])) {
+                        $attributes['entryLevelRights'][] = $method;
+                    }
+                }
+
+                break;
+
+            case "attributeLevel":
+                $attributes['attributeLevelRights'][$_acl_components[2]] = Array();
+                $_acl_value = explode(',', $_acl_value);
+
+                foreach ($_acl_value as $right) {
+                    list($method, $bool) = explode(':', $right);
+                    if ($bool == "1" && !in_array($method, $attributes['attributeLevelRights'][$_acl_components[2]])) {
+                        $attributes['attributeLevelRights'][$_acl_components[2]][] = $method;
+                    }
+                }
+
+                break;
+
+            default:
+                break;
+        }
+    }
+
     private function parse_attribute_level_rights($attribute_value)
     {
         $attribute_value  = str_replace(", ", ",", $attribute_value);
@@ -2309,6 +2482,33 @@ class Net_LDAP3
         return $control;
     }
 
+    private function vendor_name()
+    {
+        if (!empty($this->vendor_name)) {
+            return $this->vendor_name;
+        }
+
+        $this->_info("Obtaining LDAP server vendor name");
+
+        if ($result = $this->search('', '(objectclass=*)', 'base', array('vendorname'))) {
+            $result  = $result->entries(true);
+            $name = $result['']['vendorname'];
+        }
+        else {
+            $name = false;
+        }
+
+        if ($name !== false) {
+            $this->_info("Vendor name is $name");
+        } else {
+            $this->_info("No vendor name!");
+        }
+
+        $this->vendor = $name;
+
+        return $name;
+    }
+
     protected function _alert()
     {
         $this->__log(LOG_ALERT, func_get_args());
@@ -2606,6 +2806,11 @@ class Net_LDAP3
 
     /**
      * Return the search string value to be used in VLV controls
+     *
+     * @param array        $sort   List of attributes in vlv index
+     * @param array|string $search Search string or attribute => value hash
+     *
+     * @return string Search string
      */
     private function _vlv_search($sort, $search)
     {
@@ -2618,15 +2823,13 @@ class Net_LDAP3
             return;
         }
 
-        $search_suffix = $this->_fuzzy_search_suffix();
-
-        foreach ($search as $attr => $value) {
-            if (!in_array(strtolower($attr), $sort)) {
+        foreach ((array) $search as $attr => $value) {
+            if ($attr && !in_array(strtolower($attr), $sort)) {
                 $this->_debug("Cannot use VLV search using attribute not indexed: $attr (not in " . var_export($sort, true) . ")");
                 return;
             }
             else {
-                return $value . $search_suffix;
+                return $value . $this->_fuzzy_search_suffix();
             }
         }
     }
diff --git a/lib/ext/Net/LDAP3/Result.php b/lib/ext/Net/LDAP3/Result.php
index 0759df0..b89c9fd 100644
--- a/lib/ext/Net/LDAP3/Result.php
+++ b/lib/ext/Net/LDAP3/Result.php
@@ -9,9 +9,18 @@
  | Copyright (C) 2006-2014, The Roundcube Dev Team                       |
  | Copyright (C) 2012-2014, Kolab Systems AG                             |
  |                                                                       |
- | Licensed under the GNU General Public License version 3 or            |
- | any later version with exceptions for plugins.                        |
- | See the README file for a full license statement.                     |
+ | This program is free software: you can redistribute it and/or modify  |
+ | it under the terms of the GNU General Public License as published by  |
+ | the Free Software Foundation, either version 3 of the License, or     |
+ | (at your option) any later version.                                   |
+ |                                                                       |
+ | This program is distributed in the hope that it will be useful,       |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of        |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          |
+ | GNU General Public License for more details.                          |
+ |                                                                       |
+ | You should have received a copy of the GNU General Public License     |
+ | along with this program.  If not, see <http://www.gnu.org/licenses/>. |
  |                                                                       |
  | PURPOSE:                                                              |
  |   Provide advanced functionality for accessing LDAP directories       |




More information about the commits mailing list