3 commits - plugins/kolab_auth plugins/kolab_delegation

Aleksander Machniak machniak at kolabsys.com
Tue Jun 25 12:32:21 CEST 2013


 plugins/kolab_auth/kolab_auth.php                    |  119 -----
 plugins/kolab_auth/kolab_auth_ldap.php               |  403 +++++++++++++++++++
 plugins/kolab_auth/package.xml                       |   10 
 plugins/kolab_delegation/config.inc.php.dist         |   12 
 plugins/kolab_delegation/kolab_delegation_engine.php |  108 ++---
 plugins/kolab_delegation/package.xml                 |    4 
 6 files changed, 497 insertions(+), 159 deletions(-)

New commits:
commit 9a4f970d64a48956e410a1793486c4ee583eded3
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Jun 25 12:31:36 2013 +0200

    Bump version number

diff --git a/plugins/kolab_delegation/package.xml b/plugins/kolab_delegation/package.xml
index 21f3a46..d22b3f0 100644
--- a/plugins/kolab_delegation/package.xml
+++ b/plugins/kolab_delegation/package.xml
@@ -15,9 +15,9 @@
 		<email>machniak at kolabsys.com</email>
 		<active>yes</active>
 	</lead>
-	<date>2012-12-19</date>
+	<date>2013-06-25</date>
 	<version>
-		<release>0.2</release>
+		<release>0.3</release>
 		<api>0.1</api>
 	</version>
 	<stability>


commit 1eb45ac68a150cbdb013949748012ce952577a10
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Jun 25 12:31:06 2013 +0200

    Add missing options description

diff --git a/plugins/kolab_delegation/config.inc.php.dist b/plugins/kolab_delegation/config.inc.php.dist
index a242ef8..679cf00 100644
--- a/plugins/kolab_delegation/config.inc.php.dist
+++ b/plugins/kolab_delegation/config.inc.php.dist
@@ -9,6 +9,18 @@ $rcmail_config['kolab_delegation_filter'] = '(objectClass=kolabInetOrgPerson)';
 // Note: LDAP addressbook defined for kolab_auth plugin is used
 $rcmail_config['kolab_delegation_delegate_field'] = 'kolabDelegate';
 
+// User authentication ID field (from fieldmap configuration)
+// See kolab_auth plugin config
+$rcmail_config['kolab_delegation_login_field'] = 'email';
+
+// Use this fields (from fieldmap configuration) for identities
+// If the value array contains more than one field, first non-empty will be used
+// Note: These aren't LDAP attributes, but field names in config
+// Note: If there's more than one email address, as many identities will be created
+// See kolab_auth plugin config
+$rcmail_config['kolab_delegation_name_field']  = array('name', 'cn');
+$rcmail_config['kolab_delegation_email_field'] = array('email');
+
 // Remove all user identities which do not match the user's primary or alias
 // addresses and delegator's addresses
 $rcmail_config['kolab_delegation_purge_identities'] = false;


commit e69e9b90ae6d187d4038897f007d2f786c1132a3
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Jun 25 12:27:26 2013 +0200

    Make kolab_auth's LDAP class be based on new rcube_ldap_generic class.
    Move kolab_auth_ldap into separate file.
    Some improvements, including performance improvement in kolab_delegate

diff --git a/plugins/kolab_auth/kolab_auth.php b/plugins/kolab_auth/kolab_auth.php
index f511949..5579743 100644
--- a/plugins/kolab_auth/kolab_auth.php
+++ b/plugins/kolab_auth/kolab_auth.php
@@ -61,12 +61,12 @@ class kolab_auth extends rcube_plugin
             $rcmail->config->set('imap_debug', true);
             $rcmail->config->set('ldap_debug', true);
             $rcmail->config->set('smtp_debug', true);
-
         }
 
     }
 
-    public function startup($args) {
+    public function startup($args)
+    {
         // Arguments are task / action, not interested
         if (!empty($_SESSION['user_roledns'])) {
             $this->load_user_role_plugins_and_settings($_SESSION['user_roledns']);
@@ -75,7 +75,8 @@ class kolab_auth extends rcube_plugin
         return $args;
     }
 
-    public function load_user_role_plugins_and_settings($role_dns) {
+    public function load_user_role_plugins_and_settings($role_dns)
+    {
         $rcmail = rcube::get_instance();
         $this->load_config();
 
@@ -151,7 +152,8 @@ class kolab_auth extends rcube_plugin
         }
     }
 
-    public function write_log($args) {
+    public function write_log($args)
+    {
         $rcmail = rcube::get_instance();
 
         if (!$rcmail->config->get('kolab_auth_auditlog', false)) {
@@ -286,7 +288,7 @@ class kolab_auth extends rcube_plugin
         }
 
         // Find user record in LDAP
-        $record = $this->get_user_record($user, $host);
+        $record = $ldap->get_user_record($user, $host);
 
         if (empty($record)) {
             $args['abort'] = true;
@@ -309,8 +311,7 @@ class kolab_auth extends rcube_plugin
         // Login As...
         if (!empty($loginas) && $admin_login) {
             // Authenticate to LDAP
-            $dn     = rcube_ldap::dn_decode($record['ID']);
-            $result = $ldap->bind($dn, $pass);
+            $result = $ldap->bind($record['dn'], $pass);
 
             if (!$result) {
                 $args['abort'] = true;
@@ -318,32 +319,24 @@ class kolab_auth extends rcube_plugin
             }
 
             // check if the original user has/belongs to administrative role/group
-            $isadmin   = false;
-            $group     = $rcmail->config->get('kolab_auth_group');
-            $role_attr = $rcmail->config->get('kolab_auth_role');
-            $role_dn   = $rcmail->config->get('kolab_auth_role_value');
+            $isadmin = false;
+            $group   = $rcmail->config->get('kolab_auth_group');
+            $role_dn = $rcmail->config->get('kolab_auth_role_value');
 
             // check role attribute
             if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) {
-                $role_dn = $this->parse_vars($role_dn, $user, $host);
-                foreach ((array)$record[$role_attr] as $role) {
-                    if ($role == $role_dn) {
-                        $isadmin = true;
-                        break;
-                    }
+                $role_dn = $ldap->parse_vars($role_dn, $user, $host);
+                if (in_array($role_dn, (array)$record[$role_attr])) {
+                    $isadmin = true;
                 }
             }
 
             // check group
             if (!$isadmin && !empty($group)) {
-                $groups = $ldap->get_record_groups($record['ID']);
-                foreach (array_keys($groups) as $g) {
-                    if ($group == rcube_ldap::dn_decode($g)) {
-                        $isadmin = true;
-                        break;
-                    }
+                $groups = $ldap->get_user_groups($record['dn'], $user, $host);
+                if (in_array($group, $groups)) {
+                    $isadmin = true;
                 }
-
             }
 
             // Save original user login for log (see below)
@@ -358,7 +351,7 @@ class kolab_auth extends rcube_plugin
 
             // user has the privilage, get "login as" user credentials
             if ($isadmin) {
-                $record = $this->get_user_record($loginas, $host);
+                $record = $ldap->get_user_record($loginas, $host);
             }
 
             if (empty($record)) {
@@ -376,7 +369,7 @@ class kolab_auth extends rcube_plugin
 
         // Store UID and DN of logged user in session for use by other plugins
         $_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid'];
-        $_SESSION['kolab_dn']  = $record['ID']; // encoded
+        $_SESSION['kolab_dn']  = $record['dn'];
 
         // Set user login
         if ($login_attr) {
@@ -485,80 +478,10 @@ class kolab_auth extends rcube_plugin
             return null;
         }
 
-        self::$ldap = new kolab_auth_ldap_backend(
-            $addressbook,
-            $rcmail->config->get('ldap_debug'),
-            $rcmail->config->mail_domain($_SESSION['imap_host'])
-        );
+        require_once __DIR__ . '/kolab_auth_ldap.php';
 
-        $rcmail->add_shutdown_function(array(self::$ldap, 'close'));
+        self::$ldap = new kolab_auth_ldap($addressbook);
 
         return self::$ldap;
     }
-
-    /**
-     * Fetches user data from LDAP addressbook
-     */
-    private function get_user_record($user, $host)
-    {
-        $rcmail = rcube::get_instance();
-        $filter = $rcmail->config->get('kolab_auth_filter');
-        $filter = $this->parse_vars($filter, $user, $host);
-        $ldap   = self::ldap();
-
-        // reset old result
-        $ldap->reset();
-
-        // get record
-        $ldap->set_filter($filter);
-        $results = $ldap->list_records();
-
-        if (count($results->records) == 1) {
-            return $results->records[0];
-        }
-    }
-
-    /**
-     * Prepares filter query for LDAP search
-     */
-    private function parse_vars($str, $user, $host)
-    {
-        $rcmail = rcube::get_instance();
-        $domain = $rcmail->config->get('username_domain');
-
-        if (!empty($domain) && strpos($user, '@') === false) {
-            if (is_array($domain) && isset($domain[$host])) {
-                $user .= '@'.rcube_utils::parse_host($domain[$host], $host);
-            }
-            else if (is_string($domain)) {
-                $user .= '@'.rcube_utils::parse_host($domain, $host);
-            }
-        }
-
-        // replace variables in filter
-        list($u, $d) = explode('@', $user);
-        $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string
-        $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u);
-
-        return strtr($str, $replaces);
-    }
-}
-
-/**
- * Wrapper class for rcube_ldap addressbook
- */
-class kolab_auth_ldap_backend extends rcube_ldap
-{
-    function __construct($p, $debug=false, $mail_domain=null)
-    {
-        parent::__construct($p, $debug, $mail_domain);
-        $this->fieldmap['uid'] = 'uid';
-    }
-
-    function set_filter($filter)
-    {
-        if ($filter) {
-            $this->prop['filter'] = $filter;
-        }
-    }
 }
diff --git a/plugins/kolab_auth/kolab_auth_ldap.php b/plugins/kolab_auth/kolab_auth_ldap.php
new file mode 100644
index 0000000..b9e557e
--- /dev/null
+++ b/plugins/kolab_auth/kolab_auth_ldap.php
@@ -0,0 +1,403 @@
+<?php
+
+/**
+ * Kolab Authentication
+ *
+ * @version @package_version@
+ * @author Aleksander Machniak <machniak at kolabsys.com>
+ *
+ * Copyright (C) 2011-2013, Kolab Systems AG <contact at kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Wrapper class for rcube_ldap_generic
+ */
+class kolab_auth_ldap extends rcube_ldap_generic
+{
+
+    function __construct($p)
+    {
+        $rcmail = rcube::get_instance();
+
+        $this->debug    = (bool) $rcmail->config->get('ldap_debug');
+        $this->domain   = $rcmail->config->get('username_domain');
+        $this->fieldmap = $p['fieldmap'];
+        $this->fieldmap['uid'] = 'uid';
+
+        $p['attributes'] = array_values($this->fieldmap);
+
+        // Connect to the server (with bind)
+        parent::__construct($p);
+        $this->_connect();
+
+        $rcmail->add_shutdown_function(array($this, 'close'));
+    }
+
+    /**
+    * Establish a connection to the LDAP server
+    */
+    private function _connect()
+    {
+        $rcube = rcube::get_instance();
+
+        // try to connect + bind for every host configured
+        // with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable
+        // see http://www.php.net/manual/en/function.ldap-connect.php
+        foreach ((array)$this->config['hosts'] as $host) {
+            // skip host if connection failed
+            if (!$this->connect($host)) {
+                continue;
+            }
+
+            $bind_pass = $this->config['bind_pass'];
+            $bind_user = $this->config['bind_user'];
+            $bind_dn   = $this->config['bind_dn'];
+
+            if (empty($bind_pass)) {
+                $this->ready = true;
+            }
+            else {
+                if (!empty($bind_dn)) {
+                    $this->ready = $this->bind($bind_dn, $bind_pass);
+                }
+                else if (!empty($this->config['auth_cid'])) {
+                    $this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_user);
+                }
+                else {
+                    $this->ready = $this->sasl_bind($bind_user, $bind_pass);
+                }
+            }
+
+            // connection established, we're done here
+            if ($this->ready) {
+                break;
+            }
+
+        }  // end foreach hosts
+
+        if (!is_resource($this->conn)) {
+            rcube::raise_error(array('code' => 100, 'type' => 'ldap',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Could not connect to any LDAP server, last tried $host"), true);
+
+            $this->ready = false;
+        }
+
+        return $this->ready;
+    }
+
+    /**
+     * Fetches user data from LDAP addressbook
+     */
+    function get_user_record($user, $host)
+    {
+        $rcmail  = rcube::get_instance();
+        $filter  = $rcmail->config->get('kolab_auth_filter');
+        $filter  = $this->parse_vars($filter, $user, $host);
+        $base_dn = $this->parse_vars($this->config['base_dn'], $user, $host);
+        $scope   = $this->config['scope'];
+
+        // get record
+        if ($result = parent::search($base_dn, $filter, $scope, $this->attributes)) {
+            if ($result->count() == 1) {
+                $entries = $result->entries(true);
+                $dn      = key($entries);
+                $entry   = array_pop($entries);
+                $entry   = $this->field_mapping($dn, $entry);
+
+                return $entry;
+            }
+        }
+    }
+
+    /**
+     * Fetches user data from LDAP addressbook
+     */
+    function get_user_groups($dn, $user, $host)
+    {
+        if (empty($dn) || empty($this->config['groups'])) {
+            return array();
+        }
+
+        $base_dn     = $this->parse_vars($this->config['groups']['base_dn'], $user, $host);
+        $name_attr   = $this->config['groups']['name_attr'] ? $this->config['groups']['name_attr'] : 'cn';
+        $member_attr = $this->get_group_member_attr();
+        $filter      = "(member=$dn)(uniqueMember=$dn)";
+
+        if ($member_attr != 'member' && $member_attr != 'uniqueMember')
+            $filter .= "($member_attr=$dn)";
+        $filter = strtr("(|$filter)", array("\\" => "\\\\"));
+
+        $result = parent::search($base_dn, $filter, 'sub', array('dn', $name_attr));
+
+        if (!$result) {
+            return array();
+        }
+
+        $groups = array();
+        foreach ($result as $entry) {
+            $entry = rcube_ldap_generic::normalize_entry($entry);
+            if (!$entry['dn']) {
+                $entry['dn'] = $result->get_dn();
+            }
+            $groups[$entry['dn']] = $entry[$name_attr];
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Get a specific LDAP record
+     *
+     * @param string DN
+     *
+     * @return array Record data
+     */
+    function get_record($dn)
+    {
+        if (!$this->ready) {
+            return;
+        }
+
+        if ($rec = $this->get_entry($dn)) {
+            $rec = rcube_ldap_generic::normalize_entry($rec);
+            $rec = $this->field_mapping($dn, $rec);
+        }
+
+        return $rec;
+    }
+
+    /**
+     * Replace LDAP record data items
+     *
+     * @param string $dn    DN
+     * @param array  $entry LDAP entry
+     *
+     * return bool True on success, False on failure
+     */
+    function replace($dn, $entry)
+    {
+        // fields mapping
+        foreach ($this->fieldmap as $field => $attr) {
+            if (array_key_exists($field, $entry)) {
+                $entry[$attr] = $entry[$field];
+                unset($entry[$field]);
+            }
+        }
+
+        return $this->mod_replace($dn, $entry);
+    }
+
+    /**
+     * Search records (simplified version of rcube_ldap::search)
+     *
+     * @param mixed   $fields   The field name of array of field names to search in
+     * @param mixed   $value    Search value (or array of values when $fields is array)
+     * @param int     $mode     Matching mode:
+     *                          0 - partial (*abc*),
+     *                          1 - strict (=),
+     *                          2 - prefix (abc*)
+     * @param boolean $select   True if results are requested, False if count only
+     * @param array   $required List of fields that cannot be empty
+     * @param int     $limit    Number of records
+     *
+     * @return array List or false on error
+     */
+    function search($fields, $value, $mode=1, $required = array(), $limit = 0)
+    {
+        $mode = intval($mode);
+
+        // use AND operator for advanced searches
+        $filter = is_array($value) ? '(&' : '(|';
+
+        // set wildcards
+        $wp = $ws = '';
+        if (!empty($this->config['fuzzy_search']) && $mode != 1) {
+            $ws = '*';
+            if (!$mode) {
+                $wp = '*';
+            }
+        }
+
+        foreach ((array)$fields as $idx => $field) {
+            $val = is_array($value) ? $value[$idx] : $value;
+            if ($attrs = (array) $this->fieldmap[$field]) {
+                if (count($attrs) > 1)
+                    $filter .= '(|';
+                foreach ($attrs as $f)
+                    $filter .= "($f=$wp" . rcube_ldap_generic::quote_string($val) . "$ws)";
+                if (count($attrs) > 1)
+                    $filter .= ')';
+            }
+        }
+        $filter .= ')';
+
+        // add required (non empty) fields filter
+        $req_filter = '';
+
+        foreach ((array)$required as $field) {
+            if (in_array($field, (array)$fields))  // required field is already in search filter
+                continue;
+            if ($attrs = (array) $this->fieldmap[$field]) {
+                if (count($attrs) > 1)
+                    $req_filter .= '(|';
+                foreach ($attrs as $f)
+                    $req_filter .= "($f=*)";
+                if (count($attrs) > 1)
+                    $req_filter .= ')';
+            }
+        }
+
+        if (!empty($req_filter)) {
+            $filter = '(&' . $req_filter . $filter . ')';
+        }
+
+        // avoid double-wildcard if $value is empty
+        $filter = preg_replace('/\*+/', '*', $filter);
+
+        // add general filter to query
+        if (!empty($this->config['filter'])) {
+            $filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->config['filter']) . ')' . $filter . ')';
+        }
+
+        $base_dn = $this->parse_vars($this->config['base_dn'], $_SESSION['username']);
+        $scope   = $this->config['scope'];
+        $attrs   = array_values($this->fieldmap);
+        $list    = array();
+
+        if ($result = parent::search($base_dn, $filter, $scope, $attrs)) {
+            $i = 0;
+            foreach ($result as $entry) {
+                if ($limit && $limit <= $i) {
+                    break;
+                }
+                $dn = $result->get_dn();
+                $list[$dn] = $this->field_mapping($dn, $entry);
+                $i++;
+            }
+        }
+
+        return $list;
+    }
+
+    /**
+     * Set filter used in search()
+     */
+    function set_filter($filter)
+    {
+        $this->config['filter'] = $filter;
+    }
+
+    /**
+     * Maps LDAP attributes to defined fields
+     */
+    protected function field_mapping($dn, $entry)
+    {
+        $entry['dn'] = $dn;
+
+        // fields mapping
+        foreach ($this->fieldmap as $field => $attr) {
+            if (isset($entry[$attr])) {
+                $entry[$field] = $entry[$attr];
+            }
+        }
+
+        return $entry;
+    }
+
+    /**
+     * Detects group member attribute name
+     */
+    private function get_group_member_attr($object_classes = array())
+    {
+        if (empty($object_classes)) {
+            $object_classes = $this->config['groups']['object_classes'];
+        }
+        if (!empty($object_classes)) {
+            foreach ((array)$object_classes as $oc) {
+                switch (strtolower($oc)) {
+                    case 'group':
+                    case 'groupofnames':
+                    case 'kolabgroupofnames':
+                        $member_attr = 'member';
+                        break;
+
+                    case 'groupofuniquenames':
+                    case 'kolabgroupofuniquenames':
+                        $member_attr = 'uniqueMember';
+                        break;
+                }
+            }
+        }
+
+        if (!empty($member_attr)) {
+            return $member_attr;
+        }
+
+        if (!empty($this->config['groups']['member_attr'])) {
+            return $this->config['groups']['member_attr'];
+        }
+
+        return 'member';
+    }
+
+    /**
+     * Prepares filter query for LDAP search
+     */
+    function parse_vars($str, $user, $host = null)
+    {
+        if (!empty($this->domain) && strpos($user, '@') === false) {
+            if ($host && is_array($this->domain) && isset($this->domain[$host])) {
+                $user .= '@'.rcube_utils::parse_host($this->domain[$host], $host);
+            }
+            else if (is_string($this->domain)) {
+                $user .= '@'.rcube_utils::parse_host($this->domain, $host);
+            }
+        }
+
+        // replace variables in filter
+        list($u, $d) = explode('@', $user);
+        $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string
+        $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u);
+
+        return strtr($str, $replaces);
+    }
+
+    /**
+     * HTML-safe DN string encoding
+     *
+     * @param string $str DN string
+     *
+     * @return string Encoded HTML identifier string
+     */
+    static function dn_encode($str)
+    {
+        return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
+    }
+
+    /**
+     * Decodes DN string encoded with _dn_encode()
+     *
+     * @param string $str Encoded HTML identifier string
+     *
+     * @return string DN string
+     */
+    static function dn_decode($str)
+    {
+        $str = str_pad(strtr($str, '-_', '+/'), strlen($str) % 4, '=', STR_PAD_RIGHT);
+        return base64_decode($str);
+    }
+}
diff --git a/plugins/kolab_auth/package.xml b/plugins/kolab_auth/package.xml
index 2d75d83..00bc969 100644
--- a/plugins/kolab_auth/package.xml
+++ b/plugins/kolab_auth/package.xml
@@ -18,10 +18,10 @@
 		<email>machniak at kolabsys.com</email>
 		<active>yes</active>
 	</lead>
-	<date>2012-12-19</date>
+	<date>2013-06-25</date>
 	<version>
-		<release>0.6</release>
-		<api>0.1</api>
+		<release>0.7</release>
+		<api>0.2</api>
 	</version>
 	<stability>
 		<release>stable</release>
@@ -35,6 +35,10 @@
 				<tasks:replace from="@name@" to="name" type="package-info"/>
 				<tasks:replace from="@package_version@" to="version" type="package-info"/>
 			</file>
+			<file name="kolab_auth_ldap.php" role="php">
+				<tasks:replace from="@name@" to="name" type="package-info"/>
+				<tasks:replace from="@package_version@" to="version" type="package-info"/>
+			</file>
 			<file name="config.inc.php.dist" role="data"></file>
 			<file name="LICENSE" role="data"></file>
 
diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php
index 26f6b38..53813bb 100644
--- a/plugins/kolab_delegation/kolab_delegation_engine.php
+++ b/plugins/kolab_delegation/kolab_delegation_engine.php
@@ -61,25 +61,24 @@ class kolab_delegation_engine
             $delegate = $this->delegate_get($delegate);
         }
 
-        $dn   = $delegate['ID'];
-        $list = $this->list_delegates();
-        $user = $this->user();
-
+        $dn = $delegate['ID'];
         if (empty($delegate) || empty($dn)) {
             return false;
         }
 
+        $list = $this->list_delegates();
+        $user = $this->user();
+
         // add delegate to the list
         $list = array_keys((array)$list);
         $list = array_filter($list);
         if (!in_array($dn, $list)) {
             $list[] = $dn;
         }
-        $list = array_map(array('rcube_ldap', 'dn_decode'), $list);
-        $user[$this->ldap_delegate_field] = $list;
+        $list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
 
         // update user record
-        $result = $this->user_update($user);
+        $result = $this->user_update_delegates($list);
 
         // Set ACL on folders
         if ($result && !empty($acl)) {
@@ -141,11 +140,11 @@ class kolab_delegation_engine
         // remove delegate from the list
         unset($list[$dn]);
         $list = array_keys($list);
-        $list = array_map(array('rcube_ldap', 'dn_decode'), $list);
+        $list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
         $user[$this->ldap_delegate_field] = $list;
 
         // update user record
-        $result = $this->user_update($user);
+        $result = $this->user_update_delegates($list);
 
         // remove ACL
         if ($result && $acl_del) {
@@ -164,25 +163,28 @@ class kolab_delegation_engine
      */
     public function delegate_get($dn)
     {
-        $ldap = $this->ldap();
+        // use internal cache so we not query LDAP more than once per request
+        if (!isset($this->cache[$dn])) {
+            $ldap = $this->ldap();
 
-        if (!$ldap) {
-            return array();
-        }
+            if (!$ldap || empty($dn)) {
+                return array();
+            }
 
-        $ldap->reset();
+            // Get delegate
+            $user = $ldap->get_record(kolab_auth_ldap::dn_decode($dn));
 
-        // Get delegate
-        $user = $ldap->get_record($dn, true);
+            if (empty($user)) {
+                return array();
+            }
 
-        if (empty($user)) {
-            return array();
-        }
+            $delegate = $this->parse_ldap_record($user);
+            $delegate['ID'] = $dn;
 
-        $delegate = $this->parse_ldap_record($user);
-        $delegate['ID'] = $dn;
+            $this->cache[$dn] = $delegate;
+        }
 
-        return $delegate;
+        return $this->cache[$dn];
     }
 
     /**
@@ -200,13 +202,13 @@ class kolab_delegation_engine
             return array();
         }
 
-        $ldap->reset();
-
         $list = $ldap->search($this->ldap_login_field, $login, 1);
 
-        if ($list->count == 1) {
-            $user = $list->next();
-            return $this->parse_ldap_record($user);
+        if (count($list) == 1) {
+            $dn   = key($list);
+            $user = $list[$dn];
+
+            return $this->parse_ldap_record($user, $dn);
         }
     }
 
@@ -248,9 +250,8 @@ class kolab_delegation_engine
     public function list_delegates()
     {
         $result = array();
-
-        $ldap = $this->ldap();
-        $user = $this->user();
+        $ldap   = $this->ldap();
+        $user   = $this->user();
 
         if (empty($ldap) || empty($user)) {
             return array();
@@ -261,12 +262,11 @@ class kolab_delegation_engine
 
         if (!empty($delegates)) {
             foreach ((array)$delegates as $dn) {
-                $ldap->reset();
-                $delegate = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
-                $data     = $this->parse_ldap_record($delegate);
+                $delegate = $ldap->get_record($dn);
+                $data     = $this->parse_ldap_record($delegate, $dn);
 
                 if (!empty($data) && !empty($data['name'])) {
-                    $result[$delegate['ID']] = $data['name'];
+                    $result[$data['ID']] = $data['name'];
                 }
             }
         }
@@ -282,18 +282,17 @@ class kolab_delegation_engine
     public function list_delegators()
     {
         $result = array();
-
-        $ldap = $this->ldap();
+        $ldap   = $this->ldap();
 
         if (empty($ldap) || empty($this->ldap_dn)) {
             return array();
         }
 
-        $ldap->reset();
-        $list = $ldap->search($this->ldap_delegate_field, rcube_ldap::dn_decode($this->ldap_dn), 1);
+        $list = $ldap->search($this->ldap_delegate_field, $this->ldap_dn, 1);
 
-        while ($delegator = $list->iterate()) {
-            $result[$delegator['ID']] = $this->parse_ldap_record($delegator);
+        foreach ($list as $dn => $delegator) {
+            $delegator = $this->parse_ldap_record($delegator, $dn);
+            $result[$delegator['ID']] = $delegator;
         }
 
         return $result;
@@ -427,11 +426,9 @@ class kolab_delegation_engine
         $fields = array_unique(array_filter(array_merge((array)$this->ldap_name_field, (array)$this->ldap_login_field)));
         $users  = array();
 
-        $ldap->reset();
-        $ldap->set_pagesize($max);
-        $result = $ldap->search($fields, $search, $mode, true, false, (array)$this->ldap_login_field);
+        $result = $ldap->search($fields, $search, $mode, (array)$this->ldap_login_field, $max);
 
-        foreach ($result->records as $record) {
+        foreach ($result as $record) {
             $user = $this->parse_ldap_record($record);
 
             if ($user['name']) {
@@ -447,7 +444,7 @@ class kolab_delegation_engine
     /**
      * Extract delegate identifiers and pretty name from LDAP record
      */
-    private function parse_ldap_record($data)
+    private function parse_ldap_record($data, $dn = null)
     {
         $email = array();
         $uid   = $data[$this->ldap_login_field];
@@ -496,12 +493,12 @@ class kolab_delegation_engine
         }
 
         return array(
+            'ID'       => kolab_auth_ldap::dn_encode($dn),
             'uid'      => $uid,
             'name'     => $name,
             'realname' => $realname,
             'imap_uid' => $imap_uid,
             'email'    => $email,
-            'ID'       => $data['ID'],
             'organization' => $organization,
         );
     }
@@ -520,8 +517,6 @@ class kolab_delegation_engine
                 return array();
             }
 
-            $ldap->reset();
-
             // Get current user record
             $this->cache['user'] = $ldap->get_record($this->ldap_dn, true);
         }
@@ -547,27 +542,28 @@ class kolab_delegation_engine
     /**
      * Update LDAP record of current user
      *
-     * @param array User data
+     * @param array List of delegates
      */
-    public function user_update($user)
+    public function user_update_delegates($list)
     {
         $ldap = $this->ldap();
+        $pass = $this->rc->decrypt($_SESSION['password']);
 
         if (!$ldap) {
             return false;
         }
 
-        $dn   = rcube_ldap::dn_decode($this->ldap_dn);
-        $pass = $this->rc->decrypt($_SESSION['password']);
-
         // need to bind as self for sufficient privilages
-        if (!$ldap->bind($dn, $pass)) {
+        if (!$ldap->bind($this->ldap_dn, $pass)) {
             return false;
         }
 
+        $user[$this->ldap_delegate_field] = $list;
+
         unset($this->cache['user']);
-        // update user record
-        return $ldap->update($this->ldap_dn, $user);
+
+        // replace delegators list in user record
+        return $ldap->replace($this->ldap_dn, $user);
     }
 
     /**





More information about the commits mailing list