Prefs LDAP Patch to follow referrals

Kevin Hildebrand kevin@hq.ensoport.com
Fri, 12 Apr 2002 14:47:27 -0400 (EDT)


This patch takes advantage of the newly added ldap_set_rebind_proc
functionality added to PHP-4.2.0RC2.  It will only work with the latest
PHP as a result.  

This patch allows referrals to be followed properly when referencing Horde
preferences.  (For instance, our site has two LDAP servers, a master and a
slave.  Attempts to update the slave will return a referral to the master,
which must be followed to properly update the database.)

This patch also fixes a bug in the ldap driver where dirty preferences
were not being marked clean after update.

Finally, this patch replaces the fatal errors on LDAP access failure with
logged warnings, so that end users can still access their pages even if
their preferences server is down.

Enjoy!

Kevin Hildebrand
ensoport Internetworks

=============================================================================

*** lib/Prefs/ldap.php.orig	Fri Apr 12 14:34:25 2002
--- lib/Prefs/ldap.php	Mon Apr  8 17:21:17 2002
***************
*** 40,46 ****
          @var string $dn */
      var $dn = '';
  
-     
      /**
       * Constructs a new LDAP preferences object.
       *
--- 40,45 ----
***************
*** 78,83 ****
--- 77,132 ----
      }
  
      /**
+      * Callback function for LDAP referrals- this function is called when an
+      * LDAP operation returns a referral to an alternate server.
+      *
+      * @param salami $conn    The LDAP connection identifier.
+      * @param string $who     The LDAP URL which indicates the referral destination
+      */
+     function _rebind_proc($conn, $who)
+     {
+         /* Strip out the hostname we're being redirected to */
+ 
+         $who = preg_replace("'^.*://'", "", $who);
+         $who = preg_replace("':\d*$'", "", $who);
+ 
+         /* Figure out the DN of the authenticating user */
+ 
+         if (!empty($this->params['rootdn'])) {
+             $bind_dn = $this->params['rootdn'];
+         } else {
+             $bind_dn = sprintf('%s=%s,%s', $this->params['uid'],
+                                $this->params['username'],
+                                $this->params['basedn']);
+         }
+ 
+         /* Make sure the server we're being redirected to is in our list 
+            of valid servers */
+ 
+ 	if (!strstr($this->params['hostspec'], $who)) {
+             Horde::logMessage(
+                 sprintf(_("Referral target %s for DN %s is not in the authorized server list!"),
+                         $who,
+                         $bind_dn),
+                 __FILE__, __LINE__);
+             return 1;
+         }
+ 
+         $bind = @ldap_bind($conn, $bind_dn, $this->params['password']);
+         if (!$bind) {
+             Horde::logMessage(
+                 sprintf(_("Rebind to server %s:%d with DN %s failed: [%d] %s"),
+                         $this->params['hostspec'],
+                         $this->params['port'],
+                         $bind_dn,
+                         ldap_errno($this->connection),
+                         ldap_error($this->connection)),
+                 __FILE__, __LINE__);
+         }
+         return 0;
+     }
+ 
+     /**
       * Opens a connection to the LDAP server.
       *
       * @return mixed         True on success or a PEAR_Error object on failure.
***************
*** 106,114 ****
          /* Connect to the LDAP server anonymously. */
          $conn = ldap_connect($this->params['hostspec'], $this->params['port']);
          if (!$conn) {
!             Horde::fatal(new PEAR_Error(sprintf(_("Failed to open an LDAP connection to %s."), $this->params['hostspec'])), __FILE__, __LINE__);
          }
  
          /* Define the DN of the current user */
          $this->dn = sprintf('%s=%s,%s', $this->params['uid'],
                              $this->user,
--- 155,177 ----
          /* Connect to the LDAP server anonymously. */
          $conn = ldap_connect($this->params['hostspec'], $this->params['port']);
          if (!$conn) {
!             Horde::logMessage(
!                 sprintf(_("Failed to open an LDAP connection to %s."),
!                         $this->params['hostspec']),
!                 __FILE__, __LINE__);
!             return false;
          }
  
+         /* Register our callback function to handle referrals */
+ 	if (ldap_set_rebind_proc($conn, array($this,'_rebind_proc')) == false) {
+             Horde::logMessage(
+                 sprintf(_("Set rebind proc failed: [%d] %s"),
+                         ldap_errno($this->connection),
+                         ldap_error($this->connection)),
+                 __FILE__, __LINE__);
+             return false;
+ 	}
+ 
          /* Define the DN of the current user */
          $this->dn = sprintf('%s=%s,%s', $this->params['uid'],
                              $this->user,
***************
*** 131,144 ****
          $bind = @ldap_bind($this->connection, $bind_dn,
                             $this->params['password']);
          if (!$bind) {
!             Horde::fatal(new PEAR_Error(
                  sprintf(_("Bind to server %s:%d with DN %s failed: [%d] %s"),
                          $this->params['hostspec'],
                          $this->params['port'],
                          $bind_dn,
                          ldap_errno($this->connection),
!                         ldap_error($this->connection))),
                  __FILE__, __LINE__);
          }
  
          /* Search for the user's full DN. */
--- 194,209 ----
          $bind = @ldap_bind($this->connection, $bind_dn,
                             $this->params['password']);
          if (!$bind) {
!             Horde::logMessage(
                  sprintf(_("Bind to server %s:%d with DN %s failed: [%d] %s"),
                          $this->params['hostspec'],
                          $this->params['port'],
                          $bind_dn,
                          ldap_errno($this->connection),
!                         ldap_error($this->connection)),
                  __FILE__, __LINE__);
+ 
+             return false;
          }
  
          /* Search for the user's full DN. */
***************
*** 156,161 ****
--- 221,227 ----
                          ldap_errno($this->connection),
                          ldap_error($this->connection)),
                  __FILE__, __LINE__);
+             return false;
          }
  
          return true;
***************
*** 227,233 ****
          if ($search) {
              $result = ldap_get_entries($this->connection, $search);
          } else {
!             Horde::fatal(new PEAR_Error(_("Failed to connect to LDAP preferences server.")), __FILE__, __LINE__);
          }
  
          if (isset($result) && is_array($result)) {
--- 293,300 ----
          if ($search) {
              $result = ldap_get_entries($this->connection, $search);
          } else {
!             Horde::logMessage(_("Failed to connect to LDAP preferences server."), __FILE__, __LINE__);
!             return false;
          }
  
          if (isset($result) && is_array($result)) {
***************
*** 334,342 ****
              }
          }
  
          /* Send the hash to the LDAP server. */
          if (!@ldap_modify($this->connection, $this->dn, $new_values)) {
!             Horde::fatal(new PEAR_Error(sprintf(_("Unable to modify preferences: [%d] %s"), ldap_errno($this->connection), ldap_error($this->connection))), __FILE__, __LINE__);
          }
  
          unset($new_values);
--- 401,420 ----
              }
          }
  
+         $updated = true;
          /* Send the hash to the LDAP server. */
          if (!@ldap_modify($this->connection, $this->dn, $new_values)) {
!             Horde::logMessage(
!                 sprintf(_("Unable to modify preferences: [%d] %s"),
!                         ldap_errno($this->connection),
!                         ldap_error($this->connection)),
!                 __FILE__, __LINE__);
!             $updated = false;
!         } else {
!             foreach($dirty_prefs as $pref) {
!                 /* Mark this preference as "clean" now. */
!                 $this->setDirty($pref, false);
!             }
          }
  
          unset($new_values);
***************
*** 344,350 ****
          /* Attempt to cache the preferences in the session. */
          $this->cacheUpdate();
  
!         return true;
      }
  
      /**
--- 422,428 ----
          /* Attempt to cache the preferences in the session. */
          $this->cacheUpdate();
  
!         return $updated;
      }
  
      /**
***************
*** 378,382 ****
--- 456,461 ----
  
          return $app . $pref;
      }
+ 
  }
  ?>

=============================================================================

*** lib/Prefs.php.orig       Fri Apr 12 14:45:39 2002
--- lib/Prefs.php       Mon Apr  8 14:54:40 2002
***************
*** 120,126 ****
          if (class_exists($class)) {
              return new $class($user, $password, $scope, $params,
$caching);
          } else {
!             return false;
          }
      }
  
--- 120,126 ----
          if (class_exists($class)) {
              return new $class($user, $password, $scope, $params,
$caching);
          } else {
!             return new Prefs;
          }
      }