PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/functions/adLDAP/src/adLDAP.php

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