[ Index ] |
|
Code source de Horde 3.1.3 |
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 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |