[dev] improvements on Auth LDAP driver

Amith Varghese amith at xalan.com
Mon Oct 13 12:40:45 PDT 2003


Here's a patch the enhances the Auth LDAP driver by adding functionality to edit
and remove users.  In addition the driver now supports binding to the LDAP tree
using a bind dn and password.  Because many LDAP servers are set up differently
the driver can optionally use a hook so that anyone who uses the driver can
customize what attributes they want to use when an object is edited/added to
the directory.

The following files were changed:
hooks.php.dist -> Gives an example of the _horde_hook_authldap hook
conf.php.dist -> turns the hook off by default
ldap.php -> the driver itself

Please let me know if there are any questions.

Amith
-------------- next part --------------
Index: conf.php.dist
===================================================================
RCS file: /repository/horde/config/conf.php.dist,v
retrieving revision 1.62
diff -u -r1.62 conf.php.dist
--- conf.php.dist	30 Sep 2003 22:11:51 -0000	1.62
+++ conf.php.dist	13 Oct 2003 15:11:44 -0000
@@ -525,3 +525,8 @@
 // false, authentication will fail.
 // There are examples for such functions in horde/config/hooks.php.dist.
 $conf['hooks']['postauthenticate'] = false;
+
+// If this is set to true, the function _horde_hook_authldap() will be used
+// to create and set the attributes needed to add/edit/delete users by the
+// LDAP Auth driver
+$conf['hooks']['authldap'] = false;
Index: hooks.php.dist
===================================================================
RCS file: /repository/horde/config/hooks.php.dist,v
retrieving revision 1.49
diff -u -r1.49 hooks.php.dist
--- hooks.php.dist	30 Sep 2003 22:11:51 -0000	1.49
+++ hooks.php.dist	13 Oct 2003 15:12:01 -0000
@@ -256,6 +256,46 @@
     }
 }
 
+// Here is an example of creating credentials needed by the LDAP Auth driver for
+// adding/deleting/updating users.
+if (!function_exists('_horde_hook_authldap')) {
+    function _horde_hook_authldap($userID, $credentials = null)
+    {
+        $entry['dn'] = 'uid=' . $userID . ',ou=People,dc=example.com';
+        if (isset($credentials) && isset($credentials['user_fullname'])) {
+            $entry['cn'] = $credentials['user_fullname'];
+        } else {
+            $entry['cn'] = $userID;
+        }
+        $entry['sn'] = $userID;
+        $entry['objectclass'][0] = 'top';
+        $entry['objectclass'][1] = 'person';
+        $entry['objectclass'][2] = 'qmailuser';
+        $entry['objectclass'][3] = 'CourierMailACcount';
+        $entry['mailhost'] = 'mail.example.com';
+        $entry['mailMessageStore'] = '/home/mail/' . $userID;
+        $entry['homeDirectory'] = '/home/mail/' . $userID;
+        $entry['mailbox'] = $entry['homeDirectory'] . '/Maildir';
+        $entry['uid'] = $userID;
+        $entry['accountStatus'] = 'active';
+        $entry['mailQuota'] = '104857600S';
+        $entry['mail'] = $userID;
+        $entry['uidNumber'] = 501;
+        $entry['gidNumber'] = 501;
+
+        // need to check for new users (password) and edited users (user_pass_2)
+        if (isset($credentials) && isset($credentials['password'])) {
+            $entry['userPassword'] =  '{MD5}' . base64_encode(mHash(MHASH_MD5, $
+credentials['password']));
+        } else if (isset($credentials) && isset($credentials['user_pass_2'])) {
+            $entry['userPassword'] =  '{MD5}' . base64_encode(mHash(MHASH_MD5, $
+credentials['user_pass_2']));
+        }
+        $entry['deliveryMode'] = 'nolocal';
+        return $entry;
+    }
+}
+
 // Here is an example fullname hook function to set the fullname from the GECOS
 // information in the passwd file.
 if (!function_exists('_prefs_hook_fullname')) {
Index: ldap.php
===================================================================
RCS file: /repository/horde/lib/Auth/ldap.php,v
retrieving revision 1.27
diff -u -r1.27 ldap.php
--- ldap.php	24 Sep 2003 17:44:55 -0000	1.27
+++ ldap.php	13 Oct 2003 15:17:33 -0000
@@ -5,9 +5,16 @@
  *
  * Required parameters:
  * ====================
- *   'basedn'    --  The base DN for the LDAP server.
- *   'hostspec'  --  The hostname of the LDAP server.
- *   'uid'       --  The username search key.
+ *   'basedn'       --  The base DN for the LDAP server.
+ *   'hostspec'     --  The hostname of the LDAP server.
+ *   'uid'          --  The username search key.
+ *   'objectclass'  --  The objectclass filter used to search for users.  Can
+ *                      be a single objectclass or an array.
+ * 
+ * Optional parameters:
+ * ====================
+ *   'binddn'       --  The DN used to bind to the LDAP server
+ *   'passwd'       --  The password used to bind to the LDAP server
  *
  *
  * $Horde: horde/lib/Auth/ldap.php,v 1.27 2003/09/24 17:44:55 chuck Exp $
@@ -31,8 +38,8 @@
      * @var array $capabilities
      */
     var $capabilities = array('add'         => true,
-                              'update'      => false,
-                              'remove'      => false,
+                              'update'      => true,
+                              'remove'      => true,
                               'list'        => true,
                               'transparent' => false);
 
@@ -92,9 +99,24 @@
             return false;
         }
 
+        if (isset($this->_params['binddn'])) {
+            $binddn = $this->_params['binddn'];
+            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
+            if (!$bind) {
+                $this->_setAuthError(_("Could not bind to LDAP server."));
+                return false;
+            }
+        } 
+
         /* Search for the user's full DN. */
-        $search = @ldap_search($ldap, $this->_params['basedn'],
-            $this->_params['uid'] . '=' . $userID, array($this->_params['uid']));
+        $search = @ldap_search($ldap, $this->_params['basedn'], 
+                               $this->_params['uid'] . '=' . $userID, 
+                               array($this->_params['uid']));
+        if (!$search) {
+                $this->_setAuthError(_("Could not search the LDAP server."));
+                return false;
+        }
+
         $result = @ldap_get_entries($ldap, $search);
         if (is_array($result) && (count($result) > 1)) {
             $dn = $result[0]['dn'];
@@ -130,21 +152,138 @@
     {
         $ldap = @ldap_connect($this->_params['hostspec']);
 
-        $binddn = $this->_params['uid'] . '=' . $this->_params['username'] . ',' . $this->_params['basedn'];
-        $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
+        if (isset($this->_params['binddn'])) {
+            $binddn = $this->_params['binddn'];
+            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
+        } else {
+            $bind = @ldap_bind($ldap);
+        }
+
+        global $conf;
+        if (!empty($conf['hooks']['authldap'])) {
+            @include HORDE_BASE . '/config/hooks.php';
+            if (function_exists('_horde_hook_authldap')) {
+                $entry = call_user_func('_horde_hook_authldap', $userID, $credentials);
+                $dn = $entry['dn'];
+                // remove the dn entry from the array
+                unset($entry['dn']);
+            }
+        } else {
+            // Try this simple default and hope it works
+            $dn = $this->_params['uid'] . '=' . $userID . ',' . $this->_params['basedn'];
+            $entry['cn'] = $userID;
+            $entry['sn'] = $userID;
+            // password not encrypted?
+            $entry['userpassword'] = $credentials['password'];
+        }
+        $success = @ldap_add($ldap, $dn, $entry);
+
+        if (!$success) {
+           return PEAR::raiseError(_("Auth_ldap: Unable to add user ") . $userID, __FILE__, __LINE__);
+        }
+        return true;
+    }
+
+    /**
+     * Remove a set of authentication credentials.
+     *
+     * @access public
+     *
+     * @param string $userID      The userID to add.
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function removeUser($userID)
+    {
+        $ldap = @ldap_connect($this->_params['hostspec']);
+                                                                                
+        if (isset($this->_params['binddn'])) {
+            $binddn = $this->_params['binddn'];
+            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
+        } else {
+            $bind = @ldap_bind($ldap);
+        }
 
-        $dn = $this->_params['uid'] . '=' . $userID . ',' . $this->_params['basedn'];
-        $entry['objectClass'][0] = 'top';
-        $entry['objectClass'][1] = 'person';
-        $entry['cn'] = $userID;
-        $entry['sn'] = $userID;
-        $entry['userpassword'] = $credentials['password'];
-        @ldap_add($ldap, $dn, $entry);
+        global $conf;
+        if (!empty($conf['hooks']['authldap'])) {
+            @include HORDE_BASE . '/config/hooks.php';
+            if (function_exists('_horde_hook_authldap')) {
+                $entry = call_user_func('_horde_hook_authldap', $userID);
+                $dn = $entry['dn'];
+            }
+        } else {
+            // Try this simple default and hope it works
+            $dn = $this->_params['uid'] . '=' . $userID . ',' . $this->_params['basedn'];
+        }
 
+        $success = @ldap_delete($ldap,$dn);
+        if (!$success) {
+           return PEAR::raiseError(_("Auth_ldap: Unable to remove user ") . $userID, __FILE__, __LINE__);
+        }
         return true;
     }
 
     /**
+     * Update a set of authentication credentials.
+     *
+     * @access public
+     *
+     * @param string $oldID       The old userID.
+     * @param string $newID       The new userID.
+     * @param array $credentials  The new credentials
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function updateUser($oldID, $newID, $credentials)
+    {
+        $ldap = @ldap_connect($this->_params['hostspec']);
+                                                                                
+        if (isset($this->_params['binddn'])) {
+            $binddn = $this->_params['binddn'];
+            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
+        } else {
+            $bind = @ldap_bind($ldap);
+        }
+
+        global $conf;
+        if (!empty($conf['hooks']['authldap'])) {
+            @include HORDE_BASE . '/config/hooks.php';
+            if (function_exists('_horde_hook_authldap')) {
+                $entry = call_user_func('_horde_hook_authldap', $oldID);
+                $olddn = $entry['dn'];
+                $entry = call_user_func('_horde_hook_authldap', $newID);
+                $newdn = $entry['dn'];
+                unset($entry['dn']);
+            }
+        } else {
+            // Try this simple default and hope it works
+            $newdn = $this->_params['uid'] . '=' . $newID . ',' . $this->_params['basedn'];
+            $olddn = $this->_params['uid'] . '=' . $oldID . ',' . $this->_params['basedn'];
+            $entry['userpassword'] = $credentials['user_pass_2'];
+        }
+        if ($oldID != $newID) {
+            if (LDAP_OPT_PROTOCOL_VERSION == 3) {
+                ldap_rename($ldap, $olddn, $newdn, $this->_params['basedn'], true);
+
+                $success = ldap_modify($ldap, $newdn, $entry); 
+            } else {
+                $success = $this->addUser($newID, $entry);
+                if ($success) {
+                    $success = $this->removeUser($oldID);
+                } 
+            }
+        } else {
+            $success = ldap_modify($ldap, $newdn, $entry); 
+        }
+
+        if (!$success) {
+            return PEAR::raiseError(_("Auth_ldap: Unable to update user ") . $newID, __FILE__, __LINE__);
+        }
+        return true;
+    }
+
+
+    /**
      * List Users
      *
      * @access public
@@ -155,15 +294,28 @@
     {
         $ldap = @ldap_connect($this->_params['hostspec']);
 
-        $dn = $this->_params['uid'] . '=' . $this->_params['username'] . ',' . $this->_params['basedn'];
-        $bind = @ldap_bind($ldap, $dn, $this->_params['password']);
+        if (isset($this->_params['binddn'])) {
+            $dn = $this->_params['binddn'];
+            $bind = @ldap_bind($ldap, $dn, $this->_params['password']);
+        } else {
+            $bind = @ldap_bind($ldap);
+        }
+
+        if (!is_array($this->_params['objectclass'])) {
+            $filter = 'objectclass=' . $this->_params['objectclass'];
+        } else {
+            $filter = '';
+            foreach ($this->_params['objectclass'] as $objectclass) {
+                $filter = '(&' . $filter;
+                $filter .= '(objectclass=' . $objectclass . '))';
+            }            
+        }
 
-        $search = ldap_search($ldap, $this->_params['basedn'],
-                              'objectClass=person');
+        $search = ldap_search($ldap, $this->_params['basedn'], $filter);
         $entries = ldap_get_entries($ldap, $search);
         $userlist = array();
         for ($i = 0; $i < $entries['count']; $i++) {
-            $userlist[$i] = $entries[$i]['cn'][0];
+            $userlist[$i] = $entries[$i][$this->_params['uid']][0];
         }
 
         return $userlist;


More information about the dev mailing list