[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/Prefs/ -> ldap.php (source)

   1  <?php
   2  
   3  /**
   4   * Horde_String class.
   5   */
   6  require_once  'Horde/String.php';
   7  
   8  /**
   9   * Preferences storage implementation for PHP's LDAP extention.
  10   *
  11   * Required parameters:<pre>
  12   *   'basedn'    The base DN for the LDAP server.
  13   *   'hostspec'  The hostname of the LDAP server.
  14   *   'uid'       The username search key.</pre>
  15   *
  16   * Optional parameters:<pre>
  17   *   'password'  'rootdn's password for bind authentication.
  18   *   'port'      The port of the LDAP server.
  19   *               DEFAULT: 389
  20   *   'rootdn'    The DN of the root (administrative) account to bind for
  21   *               write operations.
  22   *   'username'  TODO
  23   *   'version'   The version of the LDAP protocol to use.
  24   *               DEFAULT: NONE (system default will be used)</pre>
  25   *
  26   * NOTE: parameter 'username' has been deprecated. Use 'rootdn'.
  27   *
  28   *
  29   * If setting up as the Horde preference handler in conf.php, the following
  30   * is an example configuration.
  31   * The schemas needed for ldap are in horde/scripts/ldap.
  32   *
  33   * <pre>
  34   *   $conf['prefs']['driver'] = 'ldap';
  35   *   $conf['prefs']['params']['hostspec'] = 'localhost';
  36   *   $conf['prefs']['params']['port'] = '389';
  37   *   $conf['prefs']['params']['basedn'] = 'dc=example,dc=org';
  38   *   $conf['prefs']['params']['uid'] = 'mail';
  39   * </pre>
  40   *
  41   * The following is valid but would only be necessary if users
  42   * do NOT have permission to modify their own LDAP accounts.
  43   *
  44   * <pre>
  45   *   $conf['prefs']['params']['rootdn'] = 'cn=Manager,dc=example,dc=org';
  46   *   $conf['prefs']['params']['password'] = 'password';
  47   * </pre>
  48   *
  49   *
  50   * $Horde: framework/Prefs/Prefs/ldap.php,v 1.85.10.20 2006/06/21 20:07:55 jan Exp $
  51   *
  52   * Copyright 1999-2006 Jon Parise <jon@horde.org>
  53   *
  54   * See the enclosed file COPYING for license information (LGPL). If you
  55   * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  56   *
  57   * @author  Jon Parise <jon@horde.org>
  58   * @since   Horde 1.3
  59   * @package Horde_Prefs
  60   */
  61  class Prefs_ldap extends Prefs {
  62  
  63      /**
  64       * Hash containing connection parameters.
  65       *
  66       * @var array
  67       */
  68      var $_params = array();
  69  
  70      /**
  71       * Handle for the current LDAP connection.
  72       *
  73       * @var resource
  74       */
  75      var $_connection;
  76  
  77      /**
  78       * Boolean indicating whether or not we're connected to the LDAP server.
  79       *
  80       * @var boolean
  81       */
  82      var $_connected = false;
  83  
  84      /**
  85       * String holding the user's DN.
  86       *
  87       * @var string
  88       */
  89      var $_dn = '';
  90  
  91      /**
  92       * Constructs a new LDAP preferences object.
  93       *
  94       * @param string $user      The user who owns these preferences.
  95       * @param string $password  The password associated with $user.
  96       * @param string $scope     The current application scope.
  97       * @param array $params     A hash containing connection parameters.
  98       * @param boolean $caching  Should caching be used?
  99       */
 100      function Prefs_ldap($user, $password, $scope = '',
 101                          $params = array(), $caching = false)
 102      {
 103          if (!Util::extensionExists('ldap')) {
 104              Horde::fatal(PEAR::raiseError(_("Prefs_ldap: Required LDAP extension not found.")), __FILE__, __LINE__);
 105          }
 106  
 107          $this->_user = $user;
 108          $this->_scope = $scope;
 109          $this->_params = $params;
 110          $this->_caching = $caching;
 111  
 112          /* If a valid server port has not been specified, set the default. */
 113          if (!isset($this->_params['port']) || !is_int($this->_params['port'])) {
 114              $this->_params['port'] = 389;
 115          }
 116  
 117          /* If $params['rootdn'] is empty, authenticate as the current user.
 118             Note: This assumes the user is allowed to modify their own LDAP
 119                   entry. */
 120          if (empty($this->_params['username']) &&
 121              empty($this->_params['rootdn'])) {
 122              $this->_params['username'] = $user;
 123              $this->_params['password'] = $password;
 124          }
 125  
 126          parent::Prefs();
 127      }
 128  
 129      /**
 130       * Opens a connection to the LDAP server.
 131       *
 132       * @access private
 133       *
 134       * @return mixed  True on success or a PEAR_Error object on failure.
 135       */
 136      function _connect()
 137      {
 138          /* Return if already connected. */
 139          if ($this->_connected) {
 140              return true;
 141          }
 142  
 143          Horde::assertDriverConfig($this->_params, 'prefs',
 144              array('hostspec', 'basedn', 'uid', 'password'),
 145              'preferences LDAP');
 146  
 147          /* Allow for multiple uid search fields */
 148          if (!is_array($this->_params['uid'])) {
 149              $this->_params['uid'] = array($this->_params['uid']);
 150          }
 151  
 152          /* Connect to the LDAP server anonymously. */
 153          $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']);
 154          if (!$conn) {
 155              Horde::logMessage(
 156                  sprintf('Failed to open an LDAP connection to %s.',
 157                          $this->_params['hostspec']),
 158                  __FILE__, __LINE__);
 159              return false;
 160          }
 161  
 162          /* Set the LDAP protocol version. */
 163          if (isset($this->_params['version'])) {
 164              if (!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
 165                                   $this->_params['version'])) {
 166                  Horde::logMessage(
 167                      sprintf('Set LDAP protocol version to %d failed: [%d] %s',
 168                              $this->_params['version'],
 169                              ldap_errno($this->_connection),
 170                              ldap_error($this->_connection)),
 171                              __FILE__, __LINE__);
 172              }
 173          }
 174  
 175          /* If necessary, bind to the LDAP server as the user with search
 176           * permissions. */
 177          if (!empty($this->_params['searchdn'])) {
 178              $bind = @ldap_bind($conn, $this->_params['searchdn'],
 179                                 $this->_params['searchpass']);
 180              if (!$bind) {
 181                  return PEAR::raiseError(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s',
 182                                                  $this->_params['hostspec'],
 183                                                  $this->_params['port'],
 184                                                  $this->_params['searchdn'],
 185                                                  ldap_errno($this->_connection),
 186                                                  ldap_error($this->_connection)));
 187              }
 188      }
 189  
 190          /* Register our callback function to handle referrals. */
 191          if (function_exists('ldap_set_rebind_proc') &&
 192              !ldap_set_rebind_proc($conn, array($this, '_rebindProc'))) {
 193              Horde::logMessage(
 194                  sprintf('Set rebind proc failed: [%d] %s',
 195                          ldap_errno($this->_connection),
 196                          ldap_error($this->_connection)),
 197                  __FILE__, __LINE__);
 198              return false;
 199          }
 200  
 201          /* Store the connection handle at the instance level. */
 202          $this->_connection = $conn;
 203          $this->_connected = true;
 204  
 205          if (!$this->_params['fetchdn']) {
 206              /* Define the DN of the current user. */
 207              $this->_dn = sprintf('%s=%s,%s', $this->_params['uid'][0],
 208                                   $this->_user,
 209                                   $this->_params['basedn']);
 210  
 211              /* And the DN of the authenticating user (may be the same as above). */
 212              if (!empty($this->_params['rootdn'])) {
 213                  $bind_dn = $this->_params['rootdn'];
 214              } else {
 215                  $bind_dn = sprintf('%s=%s,%s', $this->_params['uid'][0],
 216                                     $this->_params['username'],
 217                                     $this->_params['basedn']);
 218              }
 219  
 220              /* Bind to the LDAP server as the authenticating user. */
 221              $bind = @ldap_bind($this->_connection, $bind_dn,
 222                                 $this->_params['password']);
 223              if (!$bind) {
 224                  Horde::logMessage(
 225                      sprintf('Bind to server %s:%d with DN %s failed: [%d] %s',
 226                              $this->_params['hostspec'],
 227                              $this->_params['port'],
 228                              $bind_dn,
 229                              ldap_errno($this->_connection),
 230                              ldap_error($this->_connection)),
 231                      __FILE__, __LINE__);
 232                  return false;
 233              }
 234          }
 235  
 236          /* Search for the user's full DN. */
 237          foreach ($this->_params['uid'] as $uid_field) {
 238              $search = ldap_search($this->_connection, $this->_params['basedn'],
 239                                    $uid_field . '=' . $this->_user, array('dn'));
 240              if ($search) {
 241                  $result = ldap_get_entries($this->_connection, $search);
 242                  if ($result && $result['count'] > 0) {
 243                      $this->_dn = $result[0]['dn'];
 244                      if ($this->_params['fetchdn']) {
 245                          // Re-bind as the user.
 246                          $bind = @ldap_bind($this->_connection, $this->_dn,
 247                                             $this->_params['password']);
 248                      }
 249                      return true;
 250                  }
 251              }
 252          }
 253  
 254          Horde::logMessage(
 255              sprintf('Failed to retrieve user\'s DN: [%d] %s',
 256                      ldap_errno($this->_connection),
 257                      ldap_error($this->_connection)),
 258              __FILE__, __LINE__);
 259  
 260          return false;
 261      }
 262  
 263      /**
 264       * Disconnect from the LDAP server and clean up the connection.
 265       *
 266       * @access private
 267       *
 268       * @return boolean  True on success, false on failure.
 269       */
 270      function _disconnect()
 271      {
 272          if ($this->_connected) {
 273              $this->_dn = '';
 274              $this->_connected = false;
 275              return ldap_close($this->_connection);
 276          } else {
 277              return true;
 278          }
 279      }
 280  
 281      /**
 282       * Callback function for LDAP referrals.  This function is called when an
 283       * LDAP operation returns a referral to an alternate server.
 284       *
 285       * @access private
 286       *
 287       * @return integer  1 on error, 0 on success.
 288       */
 289      function _rebindProc($conn, $who)
 290      {
 291          /* Strip out the hostname we're being redirected to. */
 292          $who = preg_replace(array('|^.*://|', '|:\d*$|'), '', $who);
 293  
 294          /* Figure out the DN of the authenticating user. */
 295          if (!empty($this->_params['rootdn'])) {
 296              $bind_dn = $this->_params['rootdn'];
 297          } else {
 298              $bind_dn = sprintf('%s=%s,%s', $this->_params['uid'][0],
 299                                 $this->_params['username'],
 300                                 $this->_params['basedn']);
 301          }
 302  
 303          /* Make sure the server we're being redirected to is in our list of
 304             valid servers. */
 305          if (strpos($this->_params['hostspec'], $who) === false) {
 306              Horde::logMessage(
 307                  sprintf('Referral target %s for DN %s is not in the authorized server list!', $who, $bind_dn),
 308                  __FILE__, __LINE__);
 309              return 1;
 310          }
 311  
 312          /* Bind to the new server. */
 313          $bind = @ldap_bind($conn, $bind_dn, $this->_params['password']);
 314          if (!$bind) {
 315              Horde::logMessage(
 316                  sprintf('Rebind to server %s:%d with DN %s failed: [%d] %s',
 317                          $this->_params['hostspec'],
 318                          $this->_params['port'],
 319                          $bind_dn,
 320                          ldap_errno($this->_connection),
 321                          ldap_error($this->_connection)),
 322                  __FILE__, __LINE__);
 323          }
 324  
 325          return 0;
 326      }
 327  
 328      /**
 329       * Retrieves the requested set of preferences from the user's LDAP entry.
 330       *
 331       * @return mixed  True on success or a PEAR_Error object on failure.
 332       */
 333      function retrieve()
 334      {
 335          /* Attempt to pull the values from the session cache first. */
 336          if ($this->cacheLookup()) {
 337              return true;
 338          }
 339  
 340          /* Load defaults to make sure we have all preferences. */
 341          parent::retrieve();
 342  
 343          /* Make sure we are connected. */
 344          $this->_connect();
 345  
 346          /* Only fetch the fields for the attributes we need. */
 347          $attrs = array('hordePrefs');
 348          if (strcmp($this->_scope, 'horde') != 0) {
 349              $attrs[] = $this->_scope . 'Prefs';
 350          }
 351  
 352          /* Search for the multi-valued field containing the array of
 353             preferences. */
 354          foreach ($this->_params['uid'] as $uid_field) {
 355              $search = ldap_search($this->_connection, $this->_params['basedn'],
 356                                    $uid_field . '=' . $this->_user, $attrs);
 357              if ($search) {
 358                  $result = ldap_get_entries($this->_connection, $search);
 359                  if ($result && $result['count'] > 0) {
 360                      break;
 361                  }
 362              }
 363          }
 364          if (!isset($result)) {
 365              Horde::logMessage('Failed to connect to LDAP preferences server.', __FILE__, __LINE__);
 366          }
 367  
 368          /* ldap_get_entries() converts attribute indexes to lowercase. */
 369          $field = String::lower($this->_scope . 'prefs');
 370  
 371          if (isset($result)) {
 372              /* Set the requested values in the $this->_prefs hash based on
 373                 the contents of the LDAP result.
 374  
 375                 Preferences are stored as colon-separated name:value pairs.
 376                 Each pair is stored as its own attribute off of the multi-
 377                 value attribute named in: $this->_scope . 'Prefs'
 378  
 379                 Note that Prefs::setValue() can't be used here because of the
 380                 check for the "changeable" bit.  We want to override that
 381                 check when populating the $this->_prefs hash from the LDAP
 382                 server.
 383               */
 384  
 385              $prefs = array();
 386  
 387              /* If hordePrefs exists, merge them as the base of the prefs. */
 388              if (isset($result[0]['hordeprefs'])) {
 389                  $prefs = array_merge($prefs, $result[0]['hordeprefs']);
 390              }
 391  
 392              /* If this scope's prefs are available, merge them as will.  Give
 393               * them a higher precedence than hordePrefs. */
 394              if (strcmp($this->_scope, 'horde') != 0) {
 395                  if (isset($result[0][$field])) {
 396                      $prefs = array_merge($prefs, $result[0][$field]);
 397                  }
 398              }
 399  
 400              foreach ($prefs as $prefstr) {
 401                  /* If the string doesn't contain a colon delimiter, skip it. */
 402                  if (substr_count($prefstr, ':') == 0) {
 403                      continue;
 404                  }
 405  
 406                  /* Split the string into its name:value components. */
 407                  list($pref, $val) = split(':', $prefstr, 2);
 408  
 409                  /* Retrieve this preference. */
 410                  if (isset($this->_prefs[$pref])) {
 411                      $this->_setValue($pref, base64_decode($val), false);
 412                      $this->setDefault($pref, false);
 413                  } else {
 414                      $this->add($pref, base64_decode($val), _PREF_SHARED);
 415                  }
 416              }
 417  
 418              /* Call hooks. */
 419              $this->_callHooks();
 420          } else {
 421              Horde::logMessage('No preferences were retrieved.', __FILE__, __LINE__);
 422              return;
 423          }
 424  
 425          /* Update the session cache. */
 426          $this->cacheUpdate();
 427  
 428          return true;
 429      }
 430  
 431      /**
 432       * Stores preferences to the LDAP server.
 433       *
 434       * @return mixed  True on success or a PEAR_Error object on failure.
 435       */
 436      function store()
 437      {
 438          $updated = true;
 439  
 440          /* Check for any "dirty" preferences. If no "dirty"
 441           * preferences are found, there's no need to update the LDAP
 442           * server. Exit successfully. */
 443          $dirty_prefs = $this->_dirtyPrefs();
 444          if (!count($dirty_prefs)) {
 445              return true;
 446          }
 447  
 448          /* Make sure we are connected. */
 449          $this->_connect();
 450  
 451          /* Build a hash of the preferences and their values that need
 452           * to be stored in the LDAP server. Because we have to update
 453           * all of the values of a multi-value entry wholesale, we
 454           * can't just pick out the dirty preferences; we must update
 455           * everything. */
 456          $new_values = array();
 457          foreach (array_keys($this->_prefs) as $pref) {
 458              // Don't store locked preferences.
 459              if (!$this->isLocked($pref)) {
 460                  $entry = $pref . ':' . base64_encode($this->getValue($pref));
 461                  $field = $this->getScope($pref) . 'Prefs';
 462                  $new_values[$field][] = $entry;
 463              }
 464          }
 465  
 466          /* Entries must have the objectclasses 'top' and 'hordeperson'
 467           * to successfully store LDAP prefs. Check for both of them,
 468           * and add them if necessary. */
 469          foreach ($this->_params['uid'] as $uid_field) {
 470              $search = ldap_search($this->_connection, $this->_params['basedn'],
 471                                    $uid_field . '=' . $this->_user,
 472                                    array('objectclass'));
 473              if ($search) {
 474                  $result = ldap_get_entries($this->_connection, $search);
 475                  if ($result && $result['count'] > 0) {
 476                      $top = false;
 477                      $hordeperson = false;
 478  
 479                      for ($i = 0; $i < $result[0]['objectclass']['count']; $i++) {
 480                          if ($result[0]['objectclass'][$i] == 'top') {
 481                              $top = true;
 482                          } elseif ($result[0]['objectclass'][$i] == 'hordePerson') {
 483                              $hordeperson = true;
 484                          }
 485                      }
 486  
 487                      /* Add any missing objectclasses. */
 488                      if (!$top) {
 489                          ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top'));
 490                      }
 491  
 492                      if (!$hordeperson) {
 493                          ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson'));
 494                      }
 495                      continue;
 496                  }
 497              }
 498          }
 499  
 500          /* Send the hash to the LDAP server. */
 501          if (ldap_mod_replace($this->_connection, $this->_dn, $new_values)) {
 502              foreach ($dirty_prefs as $pref) {
 503                  $this->setDirty($pref, false);
 504              }
 505          } else {
 506              Horde::logMessage(
 507                 sprintf('Unable to modify preferences: [%d] %s',
 508                          ldap_errno($this->_connection),
 509                          ldap_error($this->_connection)),
 510                  __FILE__, __LINE__);
 511              $updated = false;
 512          }
 513  
 514          /* Attempt to cache the preferences in the session. */
 515          $this->cacheUpdate();
 516  
 517          return $updated;
 518      }
 519  
 520      /**
 521       * Clears all preferences from the LDAP backend.
 522       */
 523      function clear()
 524      {
 525          /* Make sure we are connected. */
 526          $this->_connect();
 527  
 528          $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin'));
 529          foreach ($attrs as $key => $val) {
 530              $attrs[$key] = $val . 'Prefs';
 531          }
 532  
 533          $search = ldap_read($this->_connection, $this->_dn,
 534                              'objectClass=hordePerson', $attrs, 1);
 535  
 536          if ($search) {
 537              $result = ldap_get_entries($this->_connection, $search);
 538          } else {
 539              Horde::logMessage('Failed to connect to LDAP preferences server.', __FILE__, __LINE__);
 540          }
 541  
 542          $attrs = array();
 543          if ($result) {
 544              for ($i = 0; $i < $result[0]['count']; $i++) {
 545                  $attrs[$result[0][$i]] = array();
 546              }
 547              if (!ldap_mod_del($this->_connection, $this->_dn, $attrs)) {
 548                  Horde::logMessage(
 549                      sprintf('Unable to clear preferences: [%d] %s',
 550                              ldap_errno($this->_connection),
 551                              ldap_error($this->_connection)),
 552                      __FILE__, __LINE__);
 553              }
 554          } else {
 555              Horde::logMessage(
 556                  sprintf('Unable to clear preferences: [%d] %s',
 557                          ldap_errno($this->_connection),
 558                          ldap_error($this->_connection)),
 559                  __FILE__, __LINE__);
 560          }
 561  
 562          $this->cleanup(true);
 563      }
 564  
 565      /**
 566       * Perform cleanup operations.
 567       *
 568       * @param boolean $all  Cleanup all Horde preferences.
 569       */
 570      function cleanup($all = false)
 571      {
 572          /* Close the LDAP connection. */
 573          $this->_disconnect();
 574  
 575          parent::cleanup($all);
 576      }
 577  
 578  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7