ldap.class.php 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519
  1. <?php
  2. /* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
  4. * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
  5. * Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. * or see http://www.gnu.org/
  20. */
  21. /**
  22. * \file htdocs/core/class/ldap.class.php
  23. * \brief File of class to manage LDAP features
  24. */
  25. /**
  26. * Class to manage LDAP features
  27. */
  28. class Ldap
  29. {
  30. /**
  31. * @var string Error code (or message)
  32. */
  33. public $error='';
  34. /**
  35. * @var string[] Array of error strings
  36. */
  37. public $errors = array();
  38. /**
  39. * Tableau des serveurs (IP addresses ou nom d'hotes)
  40. */
  41. var $server=array();
  42. /**
  43. * Base DN (e.g. "dc=foo,dc=com")
  44. */
  45. var $dn;
  46. /**
  47. * type de serveur, actuellement OpenLdap et Active Directory
  48. */
  49. var $serverType;
  50. /**
  51. * Version du protocole ldap
  52. */
  53. var $domain;
  54. /**
  55. * User administrateur Ldap
  56. * Active Directory ne supporte pas les connexions anonymes
  57. */
  58. var $searchUser;
  59. /**
  60. * Mot de passe de l'administrateur
  61. * Active Directory ne supporte pas les connexions anonymes
  62. */
  63. var $searchPassword;
  64. /**
  65. * DN des utilisateurs
  66. */
  67. var $people;
  68. /**
  69. * DN des groupes
  70. */
  71. var $groups;
  72. /**
  73. * Code erreur retourne par le serveur Ldap
  74. */
  75. var $ldapErrorCode;
  76. /**
  77. * Message texte de l'erreur
  78. */
  79. var $ldapErrorText;
  80. //Fetch user
  81. var $name;
  82. var $firstname;
  83. var $login;
  84. var $phone;
  85. var $skype;
  86. var $fax;
  87. var $mail;
  88. var $mobile;
  89. var $uacf;
  90. var $pwdlastset;
  91. var $ldapcharset='UTF-8'; // LDAP should be UTF-8 encoded
  92. /**
  93. * The internal LDAP connection handle
  94. */
  95. var $connection;
  96. /**
  97. * Result of any connections etc.
  98. */
  99. var $result;
  100. /**
  101. * Constructor
  102. */
  103. function __construct()
  104. {
  105. global $conf;
  106. // Server
  107. if (! empty($conf->global->LDAP_SERVER_HOST)) $this->server[] = $conf->global->LDAP_SERVER_HOST;
  108. if (! empty($conf->global->LDAP_SERVER_HOST_SLAVE)) $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE;
  109. $this->serverPort = $conf->global->LDAP_SERVER_PORT;
  110. $this->ldapProtocolVersion = $conf->global->LDAP_SERVER_PROTOCOLVERSION;
  111. $this->dn = $conf->global->LDAP_SERVER_DN;
  112. $this->serverType = $conf->global->LDAP_SERVER_TYPE;
  113. $this->domain = $conf->global->LDAP_SERVER_DN;
  114. $this->searchUser = $conf->global->LDAP_ADMIN_DN;
  115. $this->searchPassword = $conf->global->LDAP_ADMIN_PASS;
  116. $this->people = $conf->global->LDAP_USER_DN;
  117. $this->groups = $conf->global->LDAP_GROUP_DN;
  118. $this->filter = $conf->global->LDAP_FILTER_CONNECTION; // Filter on user
  119. $this->filtermember = $conf->global->LDAP_MEMBER_FILTER; // Filter on member
  120. // Users
  121. $this->attr_login = $conf->global->LDAP_FIELD_LOGIN; //unix
  122. $this->attr_sambalogin = $conf->global->LDAP_FIELD_LOGIN_SAMBA; //samba, activedirectory
  123. $this->attr_name = $conf->global->LDAP_FIELD_NAME;
  124. $this->attr_firstname = $conf->global->LDAP_FIELD_FIRSTNAME;
  125. $this->attr_mail = $conf->global->LDAP_FIELD_MAIL;
  126. $this->attr_phone = $conf->global->LDAP_FIELD_PHONE;
  127. $this->attr_skype = $conf->global->LDAP_FIELD_SKYPE;
  128. $this->attr_fax = $conf->global->LDAP_FIELD_FAX;
  129. $this->attr_mobile = $conf->global->LDAP_FIELD_MOBILE;
  130. }
  131. // Connection handling methods -------------------------------------------
  132. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  133. /**
  134. * Connect and bind
  135. * Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType, this->searchUser, this->searchPassword
  136. * After return, this->connection and $this->bind are defined
  137. *
  138. * @return int <0 if KO, 1 if bind anonymous, 2 if bind auth
  139. */
  140. function connect_bind()
  141. {
  142. // phpcs:enable
  143. global $langs, $conf;
  144. $connected=0;
  145. $this->bind=0;
  146. // Check parameters
  147. if (count($this->server) == 0 || empty($this->server[0]))
  148. {
  149. $this->error='LDAP setup (file conf.php) is not complete';
  150. dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
  151. return -1;
  152. }
  153. if (! function_exists("ldap_connect"))
  154. {
  155. $this->error='LDAPFunctionsNotAvailableOnPHP';
  156. dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
  157. $return=-1;
  158. }
  159. if (empty($this->error))
  160. {
  161. // Loop on each ldap server
  162. foreach ($this->server as $key => $host)
  163. {
  164. if ($connected) break;
  165. if (empty($host)) continue;
  166. if ($this->serverPing($host, $this->serverPort) === true) {
  167. $this->connection = ldap_connect($host, $this->serverPort);
  168. }
  169. else continue;
  170. if (is_resource($this->connection))
  171. {
  172. // Begin TLS if requested by the configuration
  173. if (! empty($conf->global->LDAP_SERVER_USE_TLS))
  174. {
  175. if (! ldap_start_tls($this->connection))
  176. {
  177. dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING);
  178. $connected = 0;
  179. $this->close();
  180. }
  181. }
  182. // Execute the ldap_set_option here (after connect and before bind)
  183. $this->setVersion();
  184. ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true.
  185. if ($this->serverType == "activedirectory")
  186. {
  187. $result=$this->setReferrals();
  188. dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
  189. $this->result=$this->bindauth($this->searchUser,$this->searchPassword);
  190. if ($this->result)
  191. {
  192. $this->bind=$this->result;
  193. $connected=2;
  194. break;
  195. }
  196. else
  197. {
  198. $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
  199. }
  200. }
  201. else
  202. {
  203. // Try in auth mode
  204. if ($this->searchUser && $this->searchPassword)
  205. {
  206. dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
  207. $this->result=$this->bindauth($this->searchUser,$this->searchPassword);
  208. if ($this->result)
  209. {
  210. $this->bind=$this->result;
  211. $connected=2;
  212. break;
  213. }
  214. else
  215. {
  216. $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
  217. }
  218. }
  219. // Try in anonymous
  220. if (! $this->bind)
  221. {
  222. dol_syslog(get_class($this)."::connect_bind try bind on ".$host,LOG_DEBUG);
  223. $result=$this->bind();
  224. if ($result)
  225. {
  226. $this->bind=$this->result;
  227. $connected=1;
  228. break;
  229. }
  230. else
  231. {
  232. $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
  233. }
  234. }
  235. }
  236. }
  237. if (! $connected) $this->close();
  238. }
  239. }
  240. if ($connected)
  241. {
  242. $return=$connected;
  243. dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG);
  244. }
  245. else
  246. {
  247. $this->error='Failed to connect to LDAP'.($this->error?': '.$this->error:'');
  248. $return=-1;
  249. dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING);
  250. }
  251. return $return;
  252. }
  253. /**
  254. * Simply closes the connection set up earlier.
  255. * Returns true if OK, false if there was an error.
  256. *
  257. * @return boolean true or false
  258. */
  259. function close()
  260. {
  261. if ($this->connection && ! @ldap_close($this->connection))
  262. {
  263. return false;
  264. }
  265. else
  266. {
  267. return true;
  268. }
  269. }
  270. /**
  271. * Anonymously binds to the connection. After this is done,
  272. * queries and searches can be done - but read-only.
  273. *
  274. * @return boolean true or false
  275. */
  276. function bind()
  277. {
  278. if (! $this->result=@ldap_bind($this->connection))
  279. {
  280. $this->ldapErrorCode = ldap_errno($this->connection);
  281. $this->ldapErrorText = ldap_error($this->connection);
  282. $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
  283. return false;
  284. }
  285. else
  286. {
  287. return true;
  288. }
  289. }
  290. /**
  291. * Binds as an authenticated user, which usually allows for write
  292. * access. The FULL dn must be passed. For a directory manager, this is
  293. * "cn=Directory Manager" under iPlanet. For a user, it will be something
  294. * like "uid=jbloggs,ou=People,dc=foo,dc=com".
  295. *
  296. * @param string $bindDn DN
  297. * @param string $pass Password
  298. * @return boolean true or false
  299. */
  300. function bindauth($bindDn,$pass)
  301. {
  302. if (! $this->result = @ldap_bind($this->connection, $bindDn, $pass))
  303. {
  304. $this->ldapErrorCode = ldap_errno($this->connection);
  305. $this->ldapErrorText = ldap_error($this->connection);
  306. $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
  307. return false;
  308. }
  309. else
  310. {
  311. return true;
  312. }
  313. }
  314. /**
  315. * Unbind du serveur ldap.
  316. *
  317. * @return boolean true or false
  318. */
  319. function unbind()
  320. {
  321. if (!$this->result=@ldap_unbind($this->connection))
  322. {
  323. return false;
  324. } else {
  325. return true;
  326. }
  327. }
  328. /**
  329. * Verification de la version du serveur ldap.
  330. *
  331. * @return string version
  332. */
  333. function getVersion()
  334. {
  335. $version = 0;
  336. $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
  337. return $version;
  338. }
  339. /**
  340. * Change ldap protocol version to use.
  341. *
  342. * @return boolean version
  343. */
  344. function setVersion()
  345. {
  346. // LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17
  347. $ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion);
  348. return $ldapsetversion;
  349. }
  350. /**
  351. * changement du referrals.
  352. *
  353. * @return boolean referrals
  354. */
  355. function setReferrals()
  356. {
  357. // LDAP_OPT_REFERRALS est une constante qui vaut ?
  358. $ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
  359. return $ldapreferrals;
  360. }
  361. /**
  362. * Add a LDAP entry
  363. * Ldap object connect and bind must have been done
  364. *
  365. * @param string $dn DN entry key
  366. * @param array $info Attributes array
  367. * @param User $user Objet user that create
  368. * @return int <0 if KO, >0 if OK
  369. */
  370. function add($dn, $info, $user)
  371. {
  372. global $conf;
  373. dol_syslog(get_class($this)."::add dn=".$dn." info=".join(',',$info));
  374. // Check parameters
  375. if (! $this->connection)
  376. {
  377. $this->error="NotConnected";
  378. return -2;
  379. }
  380. if (! $this->bind)
  381. {
  382. $this->error="NotConnected";
  383. return -3;
  384. }
  385. // Encode to LDAP page code
  386. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  387. foreach($info as $key => $val)
  388. {
  389. if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
  390. }
  391. $this->dump($dn,$info);
  392. //print_r($info);
  393. $result=@ldap_add($this->connection, $dn, $info);
  394. if ($result)
  395. {
  396. dol_syslog(get_class($this)."::add successfull", LOG_DEBUG);
  397. return 1;
  398. }
  399. else
  400. {
  401. $this->ldapErrorCode = @ldap_errno($this->connection);
  402. $this->ldapErrorText = @ldap_error($this->connection);
  403. $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
  404. dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR);
  405. return -1;
  406. }
  407. }
  408. /**
  409. * Modify a LDAP entry
  410. * Ldap object connect and bind must have been done
  411. *
  412. * @param string $dn DN entry key
  413. * @param array $info Attributes array
  414. * @param User $user Objet user that modify
  415. * @return int <0 if KO, >0 if OK
  416. */
  417. function modify($dn, $info, $user)
  418. {
  419. global $conf;
  420. dol_syslog(get_class($this)."::modify dn=".$dn." info=".join(',',$info));
  421. // Check parameters
  422. if (! $this->connection)
  423. {
  424. $this->error="NotConnected";
  425. return -2;
  426. }
  427. if (! $this->bind)
  428. {
  429. $this->error="NotConnected";
  430. return -3;
  431. }
  432. // Encode to LDAP page code
  433. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  434. foreach($info as $key => $val)
  435. {
  436. if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
  437. }
  438. $this->dump($dn,$info);
  439. //print_r($info);
  440. $result=@ldap_modify($this->connection, $dn, $info);
  441. if ($result)
  442. {
  443. dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG);
  444. return 1;
  445. }
  446. else
  447. {
  448. $this->error=@ldap_error($this->connection);
  449. dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR);
  450. return -1;
  451. }
  452. }
  453. /**
  454. * Rename a LDAP entry
  455. * Ldap object connect and bind must have been done
  456. *
  457. * @param string $dn Old DN entry key (uid=qqq,ou=xxx,dc=aaa,dc=bbb) (before update)
  458. * @param string $newrdn New RDN entry key (uid=qqq)
  459. * @param string $newparent New parent (ou=xxx,dc=aaa,dc=bbb)
  460. * @param User $user Objet user that modify
  461. * @param bool $deleteoldrdn If true the old RDN value(s) is removed, else the old RDN value(s) is retained as non-distinguished values of the entry.
  462. * @return int <0 if KO, >0 if OK
  463. */
  464. function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true)
  465. {
  466. global $conf;
  467. dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn?1:0));
  468. // Check parameters
  469. if (! $this->connection)
  470. {
  471. $this->error="NotConnected";
  472. return -2;
  473. }
  474. if (! $this->bind)
  475. {
  476. $this->error="NotConnected";
  477. return -3;
  478. }
  479. // Encode to LDAP page code
  480. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  481. $newrdn=$this->convFromOutputCharset($newrdn,$this->ldapcharset);
  482. $newparent=$this->convFromOutputCharset($newparent,$this->ldapcharset);
  483. //print_r($info);
  484. $result=@ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
  485. if ($result)
  486. {
  487. dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG);
  488. return 1;
  489. }
  490. else
  491. {
  492. $this->error=@ldap_error($this->connection);
  493. dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR);
  494. return -1;
  495. }
  496. }
  497. /**
  498. * Modify a LDAP entry (to use if dn != olddn)
  499. * Ldap object connect and bind must have been done
  500. *
  501. * @param string $dn DN entry key
  502. * @param array $info Attributes array
  503. * @param User $user Objet user that update
  504. * @param string $olddn Old DN entry key (before update)
  505. * @param string $newrdn New RDN entry key (uid=qqq) (for ldap_rename)
  506. * @param string $newparent New parent (ou=xxx,dc=aaa,dc=bbb) (for ldap_rename)
  507. * @return int <0 if KO, >0 if OK
  508. */
  509. function update($dn, $info, $user, $olddn, $newrdn=false, $newparent=false)
  510. {
  511. global $conf;
  512. dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn);
  513. // Check parameters
  514. if (! $this->connection)
  515. {
  516. $this->error="NotConnected";
  517. return -2;
  518. }
  519. if (! $this->bind)
  520. {
  521. $this->error="NotConnected";
  522. return -3;
  523. }
  524. if (! $olddn || $olddn != $dn)
  525. {
  526. if (! empty($olddn) && ! empty($newrdn) && ! empty($newparent) && $conf->global->LDAP_SERVER_PROTOCOLVERSION === '3')
  527. {
  528. // This function currently only works with LDAPv3
  529. $result = $this->rename($olddn, $newrdn, $newparent, $user, true);
  530. }
  531. else
  532. {
  533. // If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one.
  534. $result = $this->add($dn, $info, $user);
  535. if ($result > 0 && $olddn && $olddn != $dn) $result = $this->delete($olddn); // If add fails, we do not try to delete old one
  536. }
  537. }
  538. else
  539. {
  540. //$result = $this->delete($olddn);
  541. $result = $this->add($dn, $info, $user); // If record has been deleted from LDAP, we recreate it. We ignore error if it already exists.
  542. $result = $this->modify($dn, $info, $user); // We use add/modify instead of delete/add when olddn is received
  543. }
  544. if ($result <= 0)
  545. {
  546. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection)." ".$this->error;
  547. dol_syslog(get_class($this)."::update ".$this->error,LOG_ERR);
  548. //print_r($info);
  549. return -1;
  550. }
  551. else
  552. {
  553. dol_syslog(get_class($this)."::update done successfully");
  554. return 1;
  555. }
  556. }
  557. /**
  558. * Delete a LDAP entry
  559. * Ldap object connect and bind must have been done
  560. *
  561. * @param string $dn DN entry key
  562. * @return int <0 if KO, >0 if OK
  563. */
  564. function delete($dn)
  565. {
  566. global $conf;
  567. dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn);
  568. // Check parameters
  569. if (! $this->connection)
  570. {
  571. $this->error="NotConnected";
  572. return -2;
  573. }
  574. if (! $this->bind)
  575. {
  576. $this->error="NotConnected";
  577. return -3;
  578. }
  579. // Encode to LDAP page code
  580. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  581. $result=@ldap_delete($this->connection, $dn);
  582. if ($result) return 1;
  583. return -1;
  584. }
  585. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  586. /**
  587. * Build a LDAP message
  588. *
  589. * @param string $dn DN entry key
  590. * @param array $info Attributes array
  591. * @return string Content of file
  592. */
  593. function dump_content($dn, $info)
  594. {
  595. // phpcs:enable
  596. $content='';
  597. // Create file content
  598. if (preg_match('/^ldap/',$this->server[0]))
  599. {
  600. $target="-H ".join(',',$this->server);
  601. }
  602. else
  603. {
  604. $target="-h ".join(',',$this->server)." -p ".$this->serverPort;
  605. }
  606. $content.="# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
  607. $content.="# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
  608. $content.="# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
  609. if (in_array('localhost',$this->server)) $content.="# If commands fails to connect, try without -h and -p\n";
  610. $content.="dn: ".$dn."\n";
  611. foreach($info as $key => $value)
  612. {
  613. if (! is_array($value))
  614. {
  615. $content.="$key: $value\n";
  616. }
  617. else
  618. {
  619. foreach($value as $valuekey => $valuevalue)
  620. {
  621. $content.="$key: $valuevalue\n";
  622. }
  623. }
  624. }
  625. return $content;
  626. }
  627. /**
  628. * Dump a LDAP message to ldapinput.in file
  629. *
  630. * @param string $dn DN entry key
  631. * @param array $info Attributes array
  632. * @return int <0 if KO, >0 if OK
  633. */
  634. function dump($dn, $info)
  635. {
  636. global $conf;
  637. // Create content
  638. $content=$this->dump_content($dn, $info);
  639. //Create file
  640. $result=dol_mkdir($conf->ldap->dir_temp);
  641. $outputfile=$conf->ldap->dir_temp.'/ldapinput.in';
  642. $fp=fopen($outputfile,"w");
  643. if ($fp)
  644. {
  645. fputs($fp, $content);
  646. fclose($fp);
  647. if (! empty($conf->global->MAIN_UMASK))
  648. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  649. return 1;
  650. }
  651. else
  652. {
  653. return -1;
  654. }
  655. }
  656. /**
  657. * Ping a server before ldap_connect for avoid waiting
  658. *
  659. * @param string $host Server host or address
  660. * @param int $port Server port (default 389)
  661. * @param int $timeout Timeout in second (default 1s)
  662. * @return boolean true or false
  663. */
  664. function serverPing($host, $port=389, $timeout=1)
  665. {
  666. // Replace ldaps:// by ssl://
  667. if (preg_match('/^ldaps:\/\/([^\/]+)\/?$/',$host, $regs)) {
  668. $host = 'ssl://'.$regs[1];
  669. }
  670. // Remove ldap://
  671. if (preg_match('/^ldap:\/\/([^\/]+)\/?$/',$host, $regs)) {
  672. $host = $regs[1];
  673. }
  674. $op = @fsockopen($host, $port, $errno, $errstr, $timeout);
  675. if (!$op) return false; //DC is N/A
  676. else {
  677. fclose($op); //explicitly close open socket connection
  678. return true; //DC is up & running, we can safely connect with ldap_connect
  679. }
  680. }
  681. // Attribute methods -----------------------------------------------------
  682. /**
  683. * Add a LDAP attribute in entry
  684. * Ldap object connect and bind must have been done
  685. *
  686. * @param string $dn DN entry key
  687. * @param array $info Attributes array
  688. * @param User $user Objet user that create
  689. * @return int <0 if KO, >0 if OK
  690. */
  691. function addAttribute($dn, $info, $user)
  692. {
  693. global $conf;
  694. dol_syslog(get_class($this)."::addAttribute dn=".$dn." info=".join(',',$info));
  695. // Check parameters
  696. if (! $this->connection)
  697. {
  698. $this->error="NotConnected";
  699. return -2;
  700. }
  701. if (! $this->bind)
  702. {
  703. $this->error="NotConnected";
  704. return -3;
  705. }
  706. // Encode to LDAP page code
  707. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  708. foreach($info as $key => $val)
  709. {
  710. if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
  711. }
  712. $this->dump($dn,$info);
  713. //print_r($info);
  714. $result=@ldap_mod_add($this->connection, $dn, $info);
  715. if ($result)
  716. {
  717. dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG);
  718. return 1;
  719. }
  720. else
  721. {
  722. $this->error=@ldap_error($this->connection);
  723. dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR);
  724. return -1;
  725. }
  726. }
  727. /**
  728. * Update a LDAP attribute in entry
  729. * Ldap object connect and bind must have been done
  730. *
  731. * @param string $dn DN entry key
  732. * @param array $info Attributes array
  733. * @param User $user Objet user that create
  734. * @return int <0 if KO, >0 if OK
  735. */
  736. function updateAttribute($dn, $info, $user)
  737. {
  738. global $conf;
  739. dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',',$info));
  740. // Check parameters
  741. if (! $this->connection)
  742. {
  743. $this->error="NotConnected";
  744. return -2;
  745. }
  746. if (! $this->bind)
  747. {
  748. $this->error="NotConnected";
  749. return -3;
  750. }
  751. // Encode to LDAP page code
  752. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  753. foreach($info as $key => $val)
  754. {
  755. if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
  756. }
  757. $this->dump($dn,$info);
  758. //print_r($info);
  759. $result=@ldap_mod_replace($this->connection, $dn, $info);
  760. if ($result)
  761. {
  762. dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG);
  763. return 1;
  764. }
  765. else
  766. {
  767. $this->error=@ldap_error($this->connection);
  768. dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR);
  769. return -1;
  770. }
  771. }
  772. /**
  773. * Delete a LDAP attribute in entry
  774. * Ldap object connect and bind must have been done
  775. *
  776. * @param string $dn DN entry key
  777. * @param array $info Attributes array
  778. * @param User $user Objet user that create
  779. * @return int <0 if KO, >0 if OK
  780. */
  781. function deleteAttribute($dn, $info, $user)
  782. {
  783. global $conf;
  784. dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',',$info));
  785. // Check parameters
  786. if (! $this->connection)
  787. {
  788. $this->error="NotConnected";
  789. return -2;
  790. }
  791. if (! $this->bind)
  792. {
  793. $this->error="NotConnected";
  794. return -3;
  795. }
  796. // Encode to LDAP page code
  797. $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
  798. foreach($info as $key => $val)
  799. {
  800. if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
  801. }
  802. $this->dump($dn,$info);
  803. //print_r($info);
  804. $result=@ldap_mod_del($this->connection, $dn, $info);
  805. if ($result)
  806. {
  807. dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG);
  808. return 1;
  809. }
  810. else
  811. {
  812. $this->error=@ldap_error($this->connection);
  813. dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR);
  814. return -1;
  815. }
  816. }
  817. /**
  818. * Returns an array containing attributes and values for first record
  819. *
  820. * @param string $dn DN entry key
  821. * @param string $filter Filter
  822. * @return int|array <0 or false if KO, array if OK
  823. */
  824. function getAttribute($dn,$filter)
  825. {
  826. // Check parameters
  827. if (! $this->connection)
  828. {
  829. $this->error="NotConnected";
  830. return -2;
  831. }
  832. if (! $this->bind)
  833. {
  834. $this->error="NotConnected";
  835. return -3;
  836. }
  837. $search = ldap_search($this->connection,$dn,$filter);
  838. // Only one entry should ever be returned
  839. $entry = ldap_first_entry($this->connection, $search);
  840. if (!$entry)
  841. {
  842. $this->ldapErrorCode = -1;
  843. $this->ldapErrorText = "Couldn't find entry";
  844. return 0; // Couldn't find entry...
  845. }
  846. // Get values
  847. if (! $values = ldap_get_attributes($this->connection, $entry))
  848. {
  849. $this->ldapErrorCode = ldap_errno($this->connection);
  850. $this->ldapErrorText = ldap_error($this->connection);
  851. return 0; // No matching attributes
  852. }
  853. // Return an array containing the attributes.
  854. return $values;
  855. }
  856. /**
  857. * Returns an array containing values for an attribute and for first record matching filterrecord
  858. *
  859. * @param string $filterrecord Record
  860. * @param string $attribute Attributes
  861. * @return void
  862. */
  863. function getAttributeValues($filterrecord,$attribute)
  864. {
  865. $attributes=array();
  866. $attributes[0] = $attribute;
  867. // We need to search for this user in order to get their entry.
  868. $this->result = @ldap_search($this->connection,$this->people,$filterrecord,$attributes);
  869. // Pourquoi cette ligne ?
  870. //$info = ldap_get_entries($this->connection, $this->result);
  871. // Only one entry should ever be returned (no user will have the same uid)
  872. $entry = ldap_first_entry($this->connection, $this->result);
  873. if (!$entry)
  874. {
  875. $this->ldapErrorCode = -1;
  876. $this->ldapErrorText = "Couldn't find user";
  877. return false; // Couldn't find the user...
  878. }
  879. // Get values
  880. if (! $values = @ldap_get_values($this->connection, $entry, $attribute))
  881. {
  882. $this->ldapErrorCode = ldap_errno($this->connection);
  883. $this->ldapErrorText = ldap_error($this->connection);
  884. return false; // No matching attributes
  885. }
  886. // Return an array containing the attributes.
  887. return $values;
  888. }
  889. /**
  890. * Returns an array containing a details or list of LDAP record(s)
  891. * ldapsearch -LLLx -hlocalhost -Dcn=admin,dc=parinux,dc=org -w password -b "ou=adherents,ou=people,dc=parinux,dc=org" userPassword
  892. *
  893. * @param string $search Value of fiel to search, '*' for all. Not used if $activefilter is set.
  894. * @param string $userDn DN (Ex: ou=adherents,ou=people,dc=parinux,dc=org)
  895. * @param string $useridentifier Name of key field (Ex: uid)
  896. * @param array $attributeArray Array of fields required. Note this array must also contains field $useridentifier (Ex: sn,userPassword)
  897. * @param int $activefilter '1' or 'user'=use field this->filter as filter instead of parameter $search, 'member'=use field this->filtermember as filter
  898. * @param array $attributeAsArray Array of fields wanted as an array not a string
  899. * @return array Array of [id_record][ldap_field]=value
  900. */
  901. function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter=0, $attributeAsArray=array())
  902. {
  903. $fulllist=array();
  904. dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',',$attributeArray).") activefilter=".$activefilter);
  905. // if the directory is AD, then bind first with the search user first
  906. if ($this->serverType == "activedirectory")
  907. {
  908. $this->bindauth($this->searchUser, $this->searchPassword);
  909. dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser);
  910. }
  911. // Define filter
  912. if (! empty($activefilter))
  913. {
  914. if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter)
  915. {
  916. $filter = '('.$this->filter.')';
  917. }
  918. elseif (((string) $activefilter == 'member') && $this->filter)
  919. {
  920. $filter = '('.$this->filtermember.')';
  921. }
  922. else // If this->filter is empty, make fiter on * (all)
  923. {
  924. $filter = '('.$useridentifier.'=*)';
  925. }
  926. }
  927. else
  928. {
  929. $filter = '('.$useridentifier.'='.$search.')';
  930. }
  931. if (is_array($attributeArray))
  932. {
  933. // Return list with required fields
  934. $attributeArray=array_values($attributeArray); // This is to force to have index reordered from 0 (not make ldap_search fails)
  935. dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter. " attributeArray=(".join(',',$attributeArray).")");
  936. //var_dump($attributeArray);
  937. $this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray);
  938. }
  939. else
  940. {
  941. // Return list with fields selected by default
  942. dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter);
  943. $this->result = @ldap_search($this->connection, $userDn, $filter);
  944. }
  945. if (!$this->result)
  946. {
  947. $this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection);
  948. return -1;
  949. }
  950. $info = @ldap_get_entries($this->connection, $this->result);
  951. // Warning: Dans info, les noms d'attributs sont en minuscule meme si passe
  952. // a ldap_search en majuscule !!!
  953. //print_r($info);
  954. for ($i = 0; $i < $info["count"]; $i++)
  955. {
  956. $recordid=$this->convToOutputCharset($info[$i][$useridentifier][0],$this->ldapcharset);
  957. if ($recordid)
  958. {
  959. //print "Found record with key $useridentifier=".$recordid."<br>\n";
  960. $fulllist[$recordid][$useridentifier]=$recordid;
  961. // Add to the array for each attribute in my list
  962. $num = count($attributeArray);
  963. for ($j = 0; $j < $num; $j++)
  964. {
  965. $keyattributelower=strtolower($attributeArray[$j]);
  966. //print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n";
  967. //permet de recuperer le SID avec Active Directory
  968. if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid")
  969. {
  970. $objectsid = $this->getObjectSid($recordid);
  971. $fulllist[$recordid][$attributeArray[$j]] = $objectsid;
  972. }
  973. else
  974. {
  975. if(in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) {
  976. $valueTab = array();
  977. foreach($info[$i][$keyattributelower] as $key => $value) {
  978. $valueTab[$key] = $this->convToOutputCharset($value,$this->ldapcharset);
  979. }
  980. $fulllist[$recordid][$attributeArray[$j]] = $valueTab;
  981. } else {
  982. $fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0],$this->ldapcharset);
  983. }
  984. }
  985. }
  986. }
  987. }
  988. asort($fulllist);
  989. return $fulllist;
  990. }
  991. /**
  992. * Converts a little-endian hex-number to one, that 'hexdec' can convert
  993. * Required by Active Directory
  994. *
  995. * @param string $hex Hex value
  996. * @return string Little endian
  997. */
  998. function littleEndian($hex)
  999. {
  1000. for ($x=dol_strlen($hex)-2; $x >= 0; $x=$x-2) {
  1001. $result .= substr($hex,$x,2);
  1002. }
  1003. return $result;
  1004. }
  1005. /**
  1006. * Recupere le SID de l'utilisateur
  1007. * Required by Active Directory
  1008. *
  1009. * @param string $ldapUser Login de l'utilisateur
  1010. * @return string Sid
  1011. */
  1012. function getObjectSid($ldapUser)
  1013. {
  1014. $criteria = '('.$this->getUserIdentifier().'='.$ldapUser.')';
  1015. $justthese = array("objectsid");
  1016. // if the directory is AD, then bind first with the search user first
  1017. if ($this->serverType == "activedirectory")
  1018. {
  1019. $this->bindauth($this->searchUser, $this->searchPassword);
  1020. }
  1021. $i = 0;
  1022. $searchDN = $this->people;
  1023. while ($i <= 2)
  1024. {
  1025. $ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese);
  1026. if (!$ldapSearchResult)
  1027. {
  1028. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
  1029. return -1;
  1030. }
  1031. $entry = ldap_first_entry($this->connection, $ldapSearchResult);
  1032. if (!$entry)
  1033. {
  1034. // Si pas de resultat on cherche dans le domaine
  1035. $searchDN = $this->domain;
  1036. $i++;
  1037. }
  1038. else
  1039. {
  1040. $i++;
  1041. $i++;
  1042. }
  1043. }
  1044. if ($entry)
  1045. {
  1046. $ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid");
  1047. $SIDText = $this->binSIDtoText($ldapBinary[0]);
  1048. return $SIDText;
  1049. }
  1050. else
  1051. {
  1052. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
  1053. return '?';
  1054. }
  1055. }
  1056. /**
  1057. * Returns the textual SID
  1058. * Indispensable pour Active Directory
  1059. *
  1060. * @param string $binsid Binary SID
  1061. * @return string Textual SID
  1062. */
  1063. function binSIDtoText($binsid)
  1064. {
  1065. $hex_sid=bin2hex($binsid);
  1066. $rev = hexdec(substr($hex_sid,0,2)); // Get revision-part of SID
  1067. $subcount = hexdec(substr($hex_sid,2,2)); // Get count of sub-auth entries
  1068. $auth = hexdec(substr($hex_sid,4,12)); // SECURITY_NT_AUTHORITY
  1069. $result = "$rev-$auth";
  1070. for ($x=0;$x < $subcount; $x++)
  1071. {
  1072. $result .= "-".hexdec($this->littleEndian(substr($hex_sid,16+($x*8),8))); // get all SECURITY_NT_AUTHORITY
  1073. }
  1074. return $result;
  1075. }
  1076. /**
  1077. * Fonction de recherche avec filtre
  1078. * this->connection doit etre defini donc la methode bind ou bindauth doit avoir deja ete appelee
  1079. * Ne pas utiliser pour recherche d'une liste donnee de proprietes
  1080. * car conflit majuscule-minuscule. A n'utiliser que pour les pages
  1081. * 'Fiche LDAP' qui affiche champ lisibles par defaut.
  1082. *
  1083. * @param string $checkDn DN de recherche (Ex: ou=users,cn=my-domain,cn=com)
  1084. * @param string $filter Search filter (ex: (sn=nom_personne) )
  1085. * @return array|int Array with answers (key lowercased - value)
  1086. */
  1087. function search($checkDn, $filter)
  1088. {
  1089. dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter);
  1090. $checkDn=$this->convFromOutputCharset($checkDn,$this->ldapcharset);
  1091. $filter=$this->convFromOutputCharset($filter,$this->ldapcharset);
  1092. // if the directory is AD, then bind first with the search user first
  1093. if ($this->serverType == "activedirectory") {
  1094. $this->bindauth($this->searchUser, $this->searchPassword);
  1095. }
  1096. $this->result = @ldap_search($this->connection, $checkDn, $filter);
  1097. $result = @ldap_get_entries($this->connection, $this->result);
  1098. if (! $result)
  1099. {
  1100. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
  1101. return -1;
  1102. }
  1103. else
  1104. {
  1105. ldap_free_result($this->result);
  1106. return $result;
  1107. }
  1108. }
  1109. /**
  1110. * Load all attribute of a LDAP user
  1111. *
  1112. * @param User $user User to search for. Not used if a filter is provided.
  1113. * @param string $filter Filter for search. Must start with &.
  1114. * Examples: &(objectClass=inetOrgPerson) &(objectClass=user)(objectCategory=person) &(isMemberOf=cn=Sales,ou=Groups,dc=opencsi,dc=com)
  1115. * @return int >0 if OK, <0 if KO
  1116. */
  1117. function fetch($user,$filter)
  1118. {
  1119. // Perform the search and get the entry handles
  1120. // if the directory is AD, then bind first with the search user first
  1121. if ($this->serverType == "activedirectory") {
  1122. $this->bindauth($this->searchUser, $this->searchPassword);
  1123. }
  1124. $searchDN = $this->people; // TODO Why searching in people then domain ?
  1125. $result = '';
  1126. $i=0;
  1127. while ($i <= 2)
  1128. {
  1129. dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter);
  1130. $this->result = @ldap_search($this->connection, $searchDN, $filter);
  1131. if ($this->result)
  1132. {
  1133. $result = @ldap_get_entries($this->connection, $this->result);
  1134. if ($result['count'] > 0) dol_syslog('Ldap::fetch search found '.$result['count'].' records');
  1135. else dol_syslog('Ldap::fetch search returns but found no records');
  1136. //var_dump($result);exit;
  1137. }
  1138. else
  1139. {
  1140. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
  1141. dol_syslog(get_class($this)."::fetch search fails");
  1142. return -1;
  1143. }
  1144. if (! $result)
  1145. {
  1146. // Si pas de resultat on cherche dans le domaine
  1147. $searchDN = $this->domain;
  1148. $i++;
  1149. }
  1150. else
  1151. {
  1152. break;
  1153. }
  1154. }
  1155. if (! $result)
  1156. {
  1157. $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
  1158. return -1;
  1159. }
  1160. else
  1161. {
  1162. $this->name = $this->convToOutputCharset($result[0][$this->attr_name][0],$this->ldapcharset);
  1163. $this->firstname = $this->convToOutputCharset($result[0][$this->attr_firstname][0],$this->ldapcharset);
  1164. $this->login = $this->convToOutputCharset($result[0][$this->attr_login][0],$this->ldapcharset);
  1165. $this->phone = $this->convToOutputCharset($result[0][$this->attr_phone][0],$this->ldapcharset);
  1166. $this->skype = $this->convToOutputCharset($result[0][$this->attr_skype][0],$this->ldapcharset);
  1167. $this->fax = $this->convToOutputCharset($result[0][$this->attr_fax][0],$this->ldapcharset);
  1168. $this->mail = $this->convToOutputCharset($result[0][$this->attr_mail][0],$this->ldapcharset);
  1169. $this->mobile = $this->convToOutputCharset($result[0][$this->attr_mobile][0],$this->ldapcharset);
  1170. $this->uacf = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0],$this->ldapcharset));
  1171. if (isset($result[0]["pwdlastset"][0])) // If expiration on password exists
  1172. {
  1173. $this->pwdlastset = ($result[0]["pwdlastset"][0] != 0)?$this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0],$this->ldapcharset)):0;
  1174. }
  1175. else
  1176. {
  1177. $this->pwdlastset = -1;
  1178. }
  1179. if (!$this->name && !$this->login) $this->pwdlastset = -1;
  1180. $this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0],$this->ldapcharset));
  1181. // FQDN domain
  1182. $domain = str_replace('dc=','',$this->domain);
  1183. $domain = str_replace(',','.',$domain);
  1184. $this->domainFQDN = $domain;
  1185. // Set ldapUserDn (each user can have a different dn)
  1186. //var_dump($result[0]);exit;
  1187. $this->ldapUserDN=$result[0]['dn'];
  1188. ldap_free_result($this->result);
  1189. return 1;
  1190. }
  1191. }
  1192. // helper methods
  1193. /**
  1194. * Returns the correct user identifier to use, based on the ldap server type
  1195. *
  1196. * @return string Login
  1197. */
  1198. function getUserIdentifier()
  1199. {
  1200. if ($this->serverType == "activedirectory") {
  1201. return $this->attr_sambalogin;
  1202. } else {
  1203. return $this->attr_login;
  1204. }
  1205. }
  1206. /**
  1207. * UserAccountControl Flgs to more human understandable form...
  1208. *
  1209. * @param string $uacf UACF
  1210. * @return void
  1211. */
  1212. function parseUACF($uacf)
  1213. {
  1214. //All flags array
  1215. $flags = array(
  1216. "TRUSTED_TO_AUTH_FOR_DELEGATION" => 16777216,
  1217. "PASSWORD_EXPIRED" => 8388608,
  1218. "DONT_REQ_PREAUTH" => 4194304,
  1219. "USE_DES_KEY_ONLY" => 2097152,
  1220. "NOT_DELEGATED" => 1048576,
  1221. "TRUSTED_FOR_DELEGATION" => 524288,
  1222. "SMARTCARD_REQUIRED" => 262144,
  1223. "MNS_LOGON_ACCOUNT" => 131072,
  1224. "DONT_EXPIRE_PASSWORD" => 65536,
  1225. "SERVER_TRUST_ACCOUNT" => 8192,
  1226. "WORKSTATION_TRUST_ACCOUNT" => 4096,
  1227. "INTERDOMAIN_TRUST_ACCOUNT" => 2048,
  1228. "NORMAL_ACCOUNT" => 512,
  1229. "TEMP_DUPLICATE_ACCOUNT" => 256,
  1230. "ENCRYPTED_TEXT_PWD_ALLOWED" => 128,
  1231. "PASSWD_CANT_CHANGE" => 64,
  1232. "PASSWD_NOTREQD" => 32,
  1233. "LOCKOUT" => 16,
  1234. "HOMEDIR_REQUIRED" => 8,
  1235. "ACCOUNTDISABLE" => 2,
  1236. "SCRIPT" => 1
  1237. );
  1238. //Parse flags to text
  1239. $retval = array();
  1240. while (list($flag, $val) = each($flags)) {
  1241. if ($uacf >= $val) {
  1242. $uacf -= $val;
  1243. $retval[$val] = $flag;
  1244. }
  1245. }
  1246. //Return human friendly flags
  1247. return($retval);
  1248. }
  1249. /**
  1250. * SamAccountType value to text
  1251. *
  1252. * @param string $samtype SamType
  1253. * @return string Sam string
  1254. */
  1255. function parseSAT($samtype)
  1256. {
  1257. $stypes = array(
  1258. 805306368 => "NORMAL_ACCOUNT",
  1259. 805306369 => "WORKSTATION_TRUST",
  1260. 805306370 => "INTERDOMAIN_TRUST",
  1261. 268435456 => "SECURITY_GLOBAL_GROUP",
  1262. 268435457 => "DISTRIBUTION_GROUP",
  1263. 536870912 => "SECURITY_LOCAL_GROUP",
  1264. 536870913 => "DISTRIBUTION_LOCAL_GROUP"
  1265. );
  1266. $retval = "";
  1267. while (list($sat, $val) = each($stypes)) {
  1268. if ($samtype == $sat) {
  1269. $retval = $val;
  1270. break;
  1271. }
  1272. }
  1273. if (empty($retval)) $retval = "UNKNOWN_TYPE_" . $samtype;
  1274. return($retval);
  1275. }
  1276. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1277. /**
  1278. * Convertit le temps ActiveDirectory en Unix timestamp
  1279. *
  1280. * @param string $value AD time to convert
  1281. * @return integer Unix timestamp
  1282. */
  1283. function convert_time($value)
  1284. {
  1285. // phpcs:enable
  1286. $dateLargeInt=$value; // nano secondes depuis 1601 !!!!
  1287. $secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601
  1288. $ADToUnixConvertor=((1970-1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes
  1289. $unixTimeStamp=intval($secsAfterADEpoch-$ADToUnixConvertor); // Unix time stamp
  1290. return $unixTimeStamp;
  1291. }
  1292. /**
  1293. * Convert a string into output/memory charset
  1294. *
  1295. * @param string $str String to convert
  1296. * @param string $pagecodefrom Page code of src string
  1297. * @return string Converted string
  1298. */
  1299. private function convToOutputCharset($str,$pagecodefrom='UTF-8')
  1300. {
  1301. global $conf;
  1302. if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str=utf8_encode($str);
  1303. if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str=utf8_decode($str);
  1304. return $str;
  1305. }
  1306. /**
  1307. * Convert a string from output/memory charset
  1308. *
  1309. * @param string $str String to convert
  1310. * @param string $pagecodeto Page code for result string
  1311. * @return string Converted string
  1312. */
  1313. function convFromOutputCharset($str,$pagecodeto='UTF-8')
  1314. {
  1315. global $conf;
  1316. if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str=utf8_decode($str);
  1317. if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str=utf8_encode($str);
  1318. return $str;
  1319. }
  1320. /**
  1321. * Return available value of group GID
  1322. *
  1323. * @param string $keygroup Key of group
  1324. * @return int gid number
  1325. */
  1326. function getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
  1327. {
  1328. global $conf;
  1329. if (empty($keygroup)) $keygroup='LDAP_KEY_GROUPS';
  1330. $search='('.$conf->global->$keygroup.'=*)';
  1331. $result = $this->search($this->groups,$search);
  1332. if ($result)
  1333. {
  1334. $c = $result['count'];
  1335. $gids = array();
  1336. for($i=0;$i<$c;$i++)
  1337. {
  1338. $gids[] = $result[$i]['gidnumber'][0];
  1339. }
  1340. rsort($gids);
  1341. return $gids[0]+1;
  1342. }
  1343. return 0;
  1344. }
  1345. }