1<?php 2/** 3 * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY 4 * Version 4.0.4 5 * 6 * PHP Version 5 with SSL and LDAP support 7 * 8 * Written by Scott Barnett, Richard Hyland 9 * email: scott@wiggumworld.com, adldap@richardhyland.com 10 * http://adldap.sourceforge.net/ 11 * 12 * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland 13 * 14 * We'd appreciate any improvements or additions to be submitted back 15 * to benefit the entire community :) 16 * 17 * This library is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU Lesser General Public 19 * License as published by the Free Software Foundation; either 20 * version 2.1 of the License. 21 * 22 * This library is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * Lesser General Public License for more details. 26 * 27 * @category ToolsAndUtilities 28 * @package adLDAP 29 * @author Scott Barnett, Richard Hyland 30 * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland 31 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1 32 * @revision $Revision: 169 $ 33 * @version 4.0.4 34 * @link http://adldap.sourceforge.net/ 35 */ 36 37/** 38* Main adLDAP class 39* 40* Can be initialised using $adldap = new adLDAP(); 41* 42* Something to keep in mind is that Active Directory is a permissions 43* based directory. If you bind as a domain user, you can't fetch as 44* much information on other users as you could as a domain admin. 45* 46* Before asking questions, please read the Documentation at 47* http://adldap.sourceforge.net/wiki/doku.php?id=api 48*/ 49require_once(dirname(__FILE__).'/collections/adLDAPCollection.php'); 50require_once(dirname(__FILE__).'/classes/adLDAPGroups.php'); 51require_once(dirname(__FILE__).'/classes/adLDAPUsers.php'); 52require_once(dirname(__FILE__).'/classes/adLDAPFolders.php'); 53require_once(dirname(__FILE__).'/classes/adLDAPUtils.php'); 54require_once(dirname(__FILE__).'/classes/adLDAPContacts.php'); 55require_once(dirname(__FILE__).'/classes/adLDAPExchange.php'); 56require_once(dirname(__FILE__).'/classes/adLDAPComputers.php'); 57 58class adLDAP { 59 60 /** 61 * Define the different types of account in AD 62 */ 63 const ADLDAP_NORMAL_ACCOUNT = 805306368; 64 const ADLDAP_WORKSTATION_TRUST = 805306369; 65 const ADLDAP_INTERDOMAIN_TRUST = 805306370; 66 const ADLDAP_SECURITY_GLOBAL_GROUP = 268435456; 67 const ADLDAP_DISTRIBUTION_GROUP = 268435457; 68 const ADLDAP_SECURITY_LOCAL_GROUP = 536870912; 69 const ADLDAP_DISTRIBUTION_LOCAL_GROUP = 536870913; 70 const ADLDAP_FOLDER = 'OU'; 71 const ADLDAP_CONTAINER = 'CN'; 72 73 /** 74 * The default port for LDAP non-SSL connections 75 */ 76 const ADLDAP_LDAP_PORT = '389'; 77 /** 78 * The default port for LDAPS SSL connections 79 */ 80 const ADLDAP_LDAPS_PORT = '636'; 81 82 /** 83 * The account suffix for your domain, can be set when the class is invoked 84 * 85 * @var string 86 */ 87 protected $accountSuffix = "@mydomain.local"; 88 89 /** 90 * The base dn for your domain 91 * 92 * If this is set to null then adLDAP will attempt to obtain this automatically from the rootDSE 93 * 94 * @var string 95 */ 96 protected $baseDn = "DC=mydomain,DC=local"; 97 98 /** 99 * Port used to talk to the domain controllers. 100 * 101 * @var int 102 */ 103 protected $adPort = self::ADLDAP_LDAP_PORT; 104 105 /** 106 * Weather to use OpenLDAP or not. 107 * 108 * @var int 109 */ 110 protected $openLDAP = false; 111 112 /** 113 * Array of domain controllers. Specifiy multiple controllers if you 114 * would like the class to balance the LDAP queries amongst multiple servers 115 * 116 * @var array 117 */ 118 protected $domainControllers = array("dc01.mydomain.local"); 119 120 /** 121 * Optional account with higher privileges for searching 122 * This should be set to a domain admin account 123 * 124 * @var string 125 * @var string 126 */ 127 protected $adminUsername = NULL; 128 protected $adminPassword = NULL; 129 130 /** 131 * AD does not return the primary group. http://support.microsoft.com/?kbid=321360 132 * This tweak will resolve the real primary group. 133 * Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if 134 * someone's primary group is NOT domain users, this is obviously going to mess up the results 135 * 136 * @var bool 137 */ 138 protected $realPrimaryGroup = true; 139 140 /** 141 * Use SSL (LDAPS), your server needs to be setup, please see 142 * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl 143 * 144 * @var bool 145 */ 146 protected $useSSL = false; 147 148 /** 149 * Use TLS 150 * If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa 151 * 152 * @var bool 153 */ 154 protected $useTLS = false; 155 156 /** 157 * Use SSO 158 * To indicate to adLDAP to reuse password set by the brower through NTLM or Kerberos 159 * 160 * @var bool 161 */ 162 protected $useSSO = false; 163 164 /** 165 * When querying group memberships, do it recursively 166 * eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C 167 * user_ingroup("Fred","C") will returns true with this option turned on, false if turned off 168 * 169 * @var bool 170 */ 171 protected $recursiveGroups = true; 172 173 // You should not need to edit anything below this line 174 //****************************************************************************************** 175 176 /** 177 * Connection and bind default variables 178 * 179 * @var mixed 180 * @var mixed 181 */ 182 protected $ldapConnection; 183 protected $ldapBind; 184 185 /** 186 * Get the active LDAP Connection 187 * 188 * @return resource 189 */ 190 public function getLdapConnection() { 191 if ($this->ldapConnection) { 192 return $this->ldapConnection; 193 } 194 return false; 195 } 196 197 /** 198 * Get the bind status 199 * 200 * @return bool 201 */ 202 public function getLdapBind() { 203 return $this->ldapBind; 204 } 205 206 /** 207 * Get the current base DN 208 * 209 * @return string 210 */ 211 public function getBaseDn() { 212 return $this->baseDn; 213 } 214 215 /** 216 * The group class 217 * 218 * @var adLDAPGroups 219 */ 220 protected $groupClass; 221 222 /** 223 * Get the group class interface 224 * 225 * @return adLDAPGroups 226 */ 227 public function group() { 228 if (!$this->groupClass) { 229 $this->groupClass = new adLDAPGroups($this); 230 } 231 return $this->groupClass; 232 } 233 234 /** 235 * The user class 236 * 237 * @var adLDAPUsers 238 */ 239 protected $userClass; 240 241 /** 242 * Get the userclass interface 243 * 244 * @return adLDAPUsers 245 */ 246 public function user() { 247 if (!$this->userClass) { 248 $this->userClass = new adLDAPUsers($this); 249 } 250 return $this->userClass; 251 } 252 253 /** 254 * The folders class 255 * 256 * @var adLDAPFolders 257 */ 258 protected $folderClass; 259 260 /** 261 * Get the folder class interface 262 * 263 * @return adLDAPFolders 264 */ 265 public function folder() { 266 if (!$this->folderClass) { 267 $this->folderClass = new adLDAPFolders($this); 268 } 269 return $this->folderClass; 270 } 271 272 /** 273 * The utils class 274 * 275 * @var adLDAPUtils 276 */ 277 protected $utilClass; 278 279 /** 280 * Get the utils class interface 281 * 282 * @return adLDAPUtils 283 */ 284 public function utilities() { 285 if (!$this->utilClass) { 286 $this->utilClass = new adLDAPUtils($this); 287 } 288 return $this->utilClass; 289 } 290 291 /** 292 * The contacts class 293 * 294 * @var adLDAPContacts 295 */ 296 protected $contactClass; 297 298 /** 299 * Get the contacts class interface 300 * 301 * @return adLDAPContacts 302 */ 303 public function contact() { 304 if (!$this->contactClass) { 305 $this->contactClass = new adLDAPContacts($this); 306 } 307 return $this->contactClass; 308 } 309 310 /** 311 * The exchange class 312 * 313 * @var adLDAPExchange 314 */ 315 protected $exchangeClass; 316 317 /** 318 * Get the exchange class interface 319 * 320 * @return adLDAPExchange 321 */ 322 public function exchange() { 323 if (!$this->exchangeClass) { 324 $this->exchangeClass = new adLDAPExchange($this); 325 } 326 return $this->exchangeClass; 327 } 328 329 /** 330 * The computers class 331 * 332 * @var adLDAPComputers 333 */ 334 protected $computersClass; 335 336 /** 337 * Get the computers class interface 338 * 339 * @return adLDAPComputers 340 */ 341 public function computer() { 342 if (!$this->computerClass) { 343 $this->computerClass = new adLDAPComputers($this); 344 } 345 return $this->computerClass; 346 } 347 348 /** 349 * Getters and Setters 350 */ 351 352 /** 353 * Set the account suffix 354 * 355 * @param string $accountSuffix 356 * @return void 357 */ 358 public function setAccountSuffix($accountSuffix) 359 { 360 $this->accountSuffix = $accountSuffix; 361 } 362 363 /** 364 * Get the account suffix 365 * 366 * @return string 367 */ 368 public function getAccountSuffix() 369 { 370 return $this->accountSuffix; 371 } 372 373 /** 374 * Set the domain controllers array 375 * 376 * @param array $domainControllers 377 * @return void 378 */ 379 public function setDomainControllers(array $domainControllers) 380 { 381 $this->domainControllers = $domainControllers; 382 } 383 384 /** 385 * Get the list of domain controllers 386 * 387 * @return void 388 */ 389 public function getDomainControllers() 390 { 391 return $this->domainControllers; 392 } 393 394 /** 395 * Sets the port number your domain controller communicates over 396 * 397 * @param int $adPort 398 */ 399 public function setPort($adPort) 400 { 401 $this->adPort = $adPort; 402 } 403 404 /** 405 * Gets the port number your domain controller communicates over 406 * 407 * @return int 408 */ 409 public function getPort() 410 { 411 return $this->adPort; 412 } 413 414 /** 415 * Set the username of an account with higher priviledges 416 * 417 * @param string $adminUsername 418 * @return void 419 */ 420 public function setAdminUsername($adminUsername) 421 { 422 $this->adminUsername = $adminUsername; 423 } 424 425 /** 426 * Get the username of the account with higher priviledges 427 * 428 * This will throw an exception for security reasons 429 */ 430 public function getAdminUsername() 431 { 432 throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); 433 } 434 435 /** 436 * Set the password of an account with higher priviledges 437 * 438 * @param string $adminPassword 439 * @return void 440 */ 441 public function setAdminPassword($adminPassword) 442 { 443 $this->adminPassword = $adminPassword; 444 } 445 446 /** 447 * Get the password of the account with higher priviledges 448 * 449 * This will throw an exception for security reasons 450 */ 451 public function getAdminPassword() 452 { 453 throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); 454 } 455 456 /** 457 * Set whether to detect the true primary group 458 * 459 * @param bool $realPrimaryGroup 460 * @return void 461 */ 462 public function setRealPrimaryGroup($realPrimaryGroup) 463 { 464 $this->realPrimaryGroup = $realPrimaryGroup; 465 } 466 467 /** 468 * Get the real primary group setting 469 * 470 * @return bool 471 */ 472 public function getRealPrimaryGroup() 473 { 474 return $this->realPrimaryGroup; 475 } 476 477 /** 478 * Set whether to use SSL 479 * 480 * @param bool $useSSL 481 * @return void 482 */ 483 public function setUseSSL($useSSL) 484 { 485 $this->useSSL = $useSSL; 486 // Set the default port correctly 487 if ($this->useSSL) { 488 $this->setPort(self::ADLDAP_LDAPS_PORT); 489 } else { 490 $this->setPort(self::ADLDAP_LDAP_PORT); 491 } 492 } 493 494 /** 495 * Get the SSL setting 496 * 497 * @return bool 498 */ 499 public function getUseSSL() 500 { 501 return $this->useSSL; 502 } 503 504 /** 505 * Set whether to use TLS 506 * 507 * @param bool $useTLS 508 * @return void 509 */ 510 public function setUseTLS($useTLS) 511 { 512 $this->useTLS = $useTLS; 513 } 514 515 /** 516 * Get the TLS setting 517 * 518 * @return bool 519 */ 520 public function getUseTLS() 521 { 522 return $this->useTLS; 523 } 524 525 /** 526 * Set whether to use SSO 527 * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined. 528 * 529 * @param bool $useSSO 530 * @return void 531 */ 532 public function setUseSSO($useSSO) 533 { 534 if ($useSSO === true && !$this->ldapSaslSupported()) { 535 throw new adLDAPException('No LDAP SASL support for PHP. See: http://www.php.net/ldap_sasl_bind'); 536 } 537 $this->useSSO = $useSSO; 538 } 539 540 /** 541 * Get the SSO setting 542 * 543 * @return bool 544 */ 545 public function getUseSSO() 546 { 547 return $this->useSSO; 548 } 549 550 /** 551 * Set whether to lookup recursive groups 552 * 553 * @param bool $recursiveGroups 554 * @return void 555 */ 556 public function setRecursiveGroups($recursiveGroups) 557 { 558 $this->recursiveGroups = $recursiveGroups; 559 } 560 561 /** 562 * Get the recursive groups setting 563 * 564 * @return bool 565 */ 566 public function getRecursiveGroups() 567 { 568 return $this->recursiveGroups; 569 } 570 571 572 /** 573 * Set the openLDAP to true/false 574 * 575 * @return boolean|null 576 */ 577 public function setUseOpenLDAP($useOpenLDAP) 578 { 579 $this->openLDAP = $useOpenLDAP; 580 } 581 582 /** 583 * Default Constructor 584 * 585 * Tries to bind to the AD domain over LDAP or LDAPs 586 * 587 * @param array $options Array of options to pass to the constructor 588 * @throws Exception - if unable to bind to Domain Controller 589 * @return bool 590 */ 591 function __construct($options = array()) { 592 // You can specifically overide any of the default configuration options setup above 593 if (count($options) > 0) { 594 if (array_key_exists("account_suffix", $options)) { $this->accountSuffix = $options["account_suffix"]; } 595 if (array_key_exists("base_dn", $options)) { $this->baseDn = $options["base_dn"]; } 596 if (array_key_exists("domain_controllers", $options)) { 597 if (!is_array($options["domain_controllers"])) { 598 throw new adLDAPException('[domain_controllers] option must be an array'); 599 } 600 $this->domainControllers = $options["domain_controllers"]; 601 } 602 if (array_key_exists("admin_username", $options)) { $this->adminUsername = $options["admin_username"]; } 603 if (array_key_exists("admin_password", $options)) { $this->adminPassword = $options["admin_password"]; } 604 if (array_key_exists("real_primarygroup", $options)) { $this->realPrimaryGroup = $options["real_primarygroup"]; } 605 if (array_key_exists("use_ssl", $options)) { $this->setUseSSL($options["use_ssl"]); } 606 if (array_key_exists("use_tls", $options)) { $this->useTLS = $options["use_tls"]; } 607 if (array_key_exists("recursive_groups", $options)) { $this->recursiveGroups = $options["recursive_groups"]; } 608 if (array_key_exists("ad_port", $options)) { $this->setPort($options["ad_port"]); } 609 if (array_key_exists("sso", $options)) { 610 $this->setUseSSO($options["sso"]); 611 if (!$this->ldapSaslSupported()) { 612 $this->setUseSSO(false); 613 } 614 } 615 } 616 617 if ($this->ldapSupported() === false) { 618 throw new adLDAPException('No LDAP support for PHP. See: http://www.php.net/ldap'); 619 } 620 621 return $this->connect(); 622 } 623 624 /** 625 * Default Destructor 626 * 627 * Closes the LDAP connection 628 * 629 * @return void 630 */ 631 function __destruct() { 632 $this->close(); 633 } 634 635 /** 636 * Connects and Binds to the Domain Controller 637 * 638 * @return bool 639 */ 640 public function connect() 641 { 642 // Connect to the AD/LDAP server as the username/password 643 $domainController = $this->randomController(); 644 if ($this->useSSL) { 645 $this->ldapConnection = ldap_connect("ldaps://".$domainController, $this->adPort); 646 } else { 647 $this->ldapConnection = ldap_connect($domainController, $this->adPort); 648 } 649 650 // Set some ldap options for talking to AD 651 ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); 652 ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, 0); 653 654 if ($this->useTLS) { 655 ldap_start_tls($this->ldapConnection); 656 } 657 658 // Bind as a domain admin if they've set it up 659 if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) { 660 $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername.$this->accountSuffix, $this->adminPassword); 661 if (!$this->ldapBind) { 662 if ($this->useSSL && !$this->useTLS) { 663 // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message 664 throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: '.$this->getLastError()); 665 } 666 else { 667 throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: '.$this->getLastError()); 668 } 669 } 670 } 671 if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) { 672 putenv("KRB5CCNAME=".$_SERVER['KRB5CCNAME']); 673 $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); 674 if (!$this->ldapBind) { 675 throw new adLDAPException('Rebind to Active Directory failed. AD said: '.$this->getLastError()); 676 } 677 else { 678 return true; 679 } 680 } 681 682 683 if ($this->baseDn == NULL) { 684 $this->baseDn = $this->findBaseDn(); 685 } 686 687 return true; 688 } 689 690 /** 691 * Closes the LDAP connection 692 * 693 * @return void 694 */ 695 public function close() { 696 if ($this->ldapConnection) { 697 @ldap_close($this->ldapConnection); 698 } 699 } 700 701 /** 702 * Validate a user's login credentials 703 * 704 * @param string $username A user's AD username 705 * @param string $password A user's AD password 706 * @param bool optional $preventRebind 707 * @return bool 708 */ 709 public function authenticate($username, $password, $preventRebind = false) { 710 // Prevent null binding 711 if ($username === NULL || $password === NULL) { return false; } 712 if (empty($username) || empty($password)) { return false; } 713 714 // Allow binding over SSO for Kerberos 715 if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) { 716 putenv("KRB5CCNAME=".$_SERVER['KRB5CCNAME']); 717 $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); 718 if (!$this->ldapBind) { 719 throw new adLDAPException('Rebind to Active Directory failed. AD said: '.$this->getLastError()); 720 } 721 else { 722 return true; 723 } 724 } 725 726 // Bind as the user 727 $ret = true; 728 729 //OpenLDAP? 730 if ($this->openLDAP == true) { $this->ldapBind = @ldap_bind($this->ldapConnection, "uid=".$username.$this->accountSuffix, $password); } 731 else { $this->ldapBind = @ldap_bind($this->ldapConnection, $username.$this->accountSuffix, $password); } 732 733 if (!$this->ldapBind) { 734 $ret = false; 735 } 736 737 // Cnce we've checked their details, kick back into admin mode if we have it 738 if ($this->adminUsername !== NULL && !$preventRebind) { 739 $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername.$this->accountSuffix, $this->adminPassword); 740 if (!$this->ldapBind) { 741 // This should never happen in theory 742 throw new adLDAPException('Rebind to Active Directory failed. AD said: '.$this->getLastError()); 743 } 744 } 745 746 return $ret; 747 } 748 749 /** 750 * Find the Base DN of your domain controller 751 * 752 * @return string 753 */ 754 public function findBaseDn() 755 { 756 $namingContext = $this->getRootDse(array('defaultnamingcontext')); 757 return $namingContext[0]['defaultnamingcontext'][0]; 758 } 759 760 /** 761 * Get the RootDSE properties from a domain controller 762 * 763 * @param string[] $attributes The attributes you wish to query e.g. defaultnamingcontext 764 * @return array 765 */ 766 public function getRootDse($attributes = array("*", "+")) { 767 if (!$this->ldapBind) { return (false); } 768 769 $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes); 770 $entries = @ldap_get_entries($this->ldapConnection, $sr); 771 return $entries; 772 } 773 774 /** 775 * Get last error from Active Directory 776 * 777 * This function gets the last message from Active Directory 778 * This may indeed be a 'Success' message but if you get an unknown error 779 * it might be worth calling this function to see what errors were raised 780 * 781 * return string 782 */ 783 public function getLastError() { 784 $extended_error = ""; 785 // define(LDAP_OPT_DIAGNOSTIC_MESSAGE, 0x0032) 786 @ldap_get_option($this->ldapConnection, 0x0032, $extended_error); 787 return $extended_error; 788 } 789 790 /** 791 * Detect LDAP support in php 792 * 793 * @return bool 794 */ 795 protected function ldapSupported() 796 { 797 if (!function_exists('ldap_connect')) { 798 return false; 799 } 800 return true; 801 } 802 803 /** 804 * Detect ldap_sasl_bind support in PHP 805 * 806 * @return bool 807 */ 808 protected function ldapSaslSupported() 809 { 810 if (!function_exists('ldap_sasl_bind')) { 811 return false; 812 } 813 return true; 814 } 815 816 /** 817 * Schema 818 * 819 * @param array $attributes Attributes to be queried 820 * @return array 821 */ 822 public function adldap_schema($attributes) { 823 824 // LDAP doesn't like NULL attributes, only set them if they have values 825 // If you wish to remove an attribute you should set it to a space 826 // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute 827 $mod = array(); 828 829 // Check every attribute to see if it contains 8bit characters and then UTF8 encode them 830 array_walk($attributes, array($this, 'encode8bit')); 831 832 if ($attributes["address_city"]) { $mod["l"][0] = $attributes["address_city"]; } 833 if ($attributes["address_code"]) { $mod["postalCode"][0] = $attributes["address_code"]; } 834 //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes? 835 if ($attributes["address_country"]) { $mod["c"][0] = $attributes["address_country"]; } 836 if ($attributes["address_pobox"]) { $mod["postOfficeBox"][0] = $attributes["address_pobox"]; } 837 if ($attributes["address_state"]) { $mod["st"][0] = $attributes["address_state"]; } 838 if ($attributes["address_street"]) { $mod["streetAddress"][0] = $attributes["address_street"]; } 839 if ($attributes["company"]) { $mod["company"][0] = $attributes["company"]; } 840 if ($attributes["change_password"]) { $mod["pwdLastSet"][0] = 0; } 841 if ($attributes["department"]) { $mod["department"][0] = $attributes["department"]; } 842 if ($attributes["description"]) { $mod["description"][0] = $attributes["description"]; } 843 if ($attributes["display_name"]) { $mod["displayName"][0] = $attributes["display_name"]; } 844 if ($attributes["email"]) { $mod["mail"][0] = $attributes["email"]; } 845 if ($attributes["expires"]) { $mod["accountExpires"][0] = $attributes["expires"]; } //unix epoch format? 846 if ($attributes["firstname"]) { $mod["givenName"][0] = $attributes["firstname"]; } 847 if ($attributes["home_directory"]) { $mod["homeDirectory"][0] = $attributes["home_directory"]; } 848 if ($attributes["home_drive"]) { $mod["homeDrive"][0] = $attributes["home_drive"]; } 849 if ($attributes["initials"]) { $mod["initials"][0] = $attributes["initials"]; } 850 if ($attributes["logon_name"]) { $mod["userPrincipalName"][0] = $attributes["logon_name"]; } 851 if ($attributes["manager"]) { $mod["manager"][0] = $attributes["manager"]; } //UNTESTED ***Use DistinguishedName*** 852 if ($attributes["office"]) { $mod["physicalDeliveryOfficeName"][0] = $attributes["office"]; } 853 if ($attributes["password"]) { $mod["unicodePwd"][0] = $this->user()->encodePassword($attributes["password"]); } 854 if ($attributes["profile_path"]) { $mod["profilepath"][0] = $attributes["profile_path"]; } 855 if ($attributes["script_path"]) { $mod["scriptPath"][0] = $attributes["script_path"]; } 856 if ($attributes["surname"]) { $mod["sn"][0] = $attributes["surname"]; } 857 if ($attributes["title"]) { $mod["title"][0] = $attributes["title"]; } 858 if ($attributes["telephone"]) { $mod["telephoneNumber"][0] = $attributes["telephone"]; } 859 if ($attributes["mobile"]) { $mod["mobile"][0] = $attributes["mobile"]; } 860 if ($attributes["pager"]) { $mod["pager"][0] = $attributes["pager"]; } 861 if ($attributes["ipphone"]) { $mod["ipphone"][0] = $attributes["ipphone"]; } 862 if ($attributes["web_page"]) { $mod["wWWHomePage"][0] = $attributes["web_page"]; } 863 if ($attributes["fax"]) { $mod["facsimileTelephoneNumber"][0] = $attributes["fax"]; } 864 if ($attributes["enabled"]) { $mod["userAccountControl"][0] = $attributes["enabled"]; } 865 if ($attributes["homephone"]) { $mod["homephone"][0] = $attributes["homephone"]; } 866 867 // Distribution List specific schema 868 if ($attributes["group_sendpermission"]) { $mod["dlMemSubmitPerms"][0] = $attributes["group_sendpermission"]; } 869 if ($attributes["group_rejectpermission"]) { $mod["dlMemRejectPerms"][0] = $attributes["group_rejectpermission"]; } 870 871 // Exchange Schema 872 if ($attributes["exchange_homemdb"]) { $mod["homeMDB"][0] = $attributes["exchange_homemdb"]; } 873 if ($attributes["exchange_mailnickname"]) { $mod["mailNickname"][0] = $attributes["exchange_mailnickname"]; } 874 if ($attributes["exchange_proxyaddress"]) { $mod["proxyAddresses"][0] = $attributes["exchange_proxyaddress"]; } 875 if ($attributes["exchange_usedefaults"]) { $mod["mDBUseDefaults"][0] = $attributes["exchange_usedefaults"]; } 876 if ($attributes["exchange_policyexclude"]) { $mod["msExchPoliciesExcluded"][0] = $attributes["exchange_policyexclude"]; } 877 if ($attributes["exchange_policyinclude"]) { $mod["msExchPoliciesIncluded"][0] = $attributes["exchange_policyinclude"]; } 878 if ($attributes["exchange_addressbook"]) { $mod["showInAddressBook"][0] = $attributes["exchange_addressbook"]; } 879 if ($attributes["exchange_altrecipient"]) { $mod["altRecipient"][0] = $attributes["exchange_altrecipient"]; } 880 if ($attributes["exchange_deliverandredirect"]) { $mod["deliverAndRedirect"][0] = $attributes["exchange_deliverandredirect"]; } 881 882 // This schema is designed for contacts 883 if ($attributes["exchange_hidefromlists"]) { $mod["msExchHideFromAddressLists"][0] = $attributes["exchange_hidefromlists"]; } 884 if ($attributes["contact_email"]) { $mod["targetAddress"][0] = $attributes["contact_email"]; } 885 886 //echo ("<pre>"); print_r($mod); 887 /* 888 // modifying a name is a bit fiddly 889 if ($attributes["firstname"] && $attributes["surname"]){ 890 $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"]; 891 $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"]; 892 $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"]; 893 } 894 */ 895 896 if (count($mod) == 0) { return (false); } 897 return ($mod); 898 } 899 900 /** 901 * Convert 8bit characters e.g. accented characters to UTF8 encoded characters 902 */ 903 protected function encode8Bit(&$item, $key) { 904 $encode = false; 905 if (is_string($item)) { 906 for ($i = 0; $i < strlen($item); $i++) { 907 if (ord($item[$i]) >> 7) { 908 $encode = true; 909 } 910 } 911 } 912 if ($encode === true && $key != 'password') { 913 $item = utf8_encode($item); 914 } 915 } 916 917 /** 918 * Select a random domain controller from your domain controller array 919 * 920 * @return string 921 */ 922 protected function randomController() 923 { 924 mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions 925 /*if (sizeof($this->domainControllers) > 1) { 926 $adController = $this->domainControllers[array_rand($this->domainControllers)]; 927 // Test if the controller is responding to pings 928 $ping = $this->pingController($adController); 929 if ($ping === false) { 930 // Find the current key in the domain controllers array 931 $key = array_search($adController, $this->domainControllers); 932 // Remove it so that we don't end up in a recursive loop 933 unset($this->domainControllers[$key]); 934 // Select a new controller 935 return $this->randomController(); 936 } 937 else { 938 return ($adController); 939 } 940 } */ 941 return $this->domainControllers[array_rand($this->domainControllers)]; 942 } 943 944 /** 945 * Test basic connectivity to controller 946 * 947 * @return bool 948 */ 949 protected function pingController($host) { 950 $port = $this->adPort; 951 fsockopen($host, $port, $errno, $errstr, 10); 952 if ($errno > 0) { 953 return false; 954 } 955 return true; 956 } 957 958} 959 960/** 961* adLDAP Exception Handler 962* 963* Exceptions of this type are thrown on bind failure or when SSL is required but not configured 964* Example: 965* try { 966* $adldap = new adLDAP(); 967* } 968* catch (adLDAPException $e) { 969* echo $e; 970* exit(); 971* } 972*/ 973class adLDAPException extends Exception {} 974 975?>