lib_ldap.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. <?php
  2. /**
  3. * ownCloud – LDAP lib
  4. *
  5. * @author Arthur Schiwon
  6. * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember');
  23. define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn');
  24. //needed to unbind, because we use OC_LDAP only statically
  25. class OC_LDAP_DESTRUCTOR {
  26. public function __destruct() {
  27. OC_LDAP::destruct();
  28. }
  29. }
  30. class OC_LDAP {
  31. static protected $ldapConnectionRes = false;
  32. static protected $configured = false;
  33. //cached settings
  34. static protected $ldapHost;
  35. static protected $ldapPort;
  36. static protected $ldapBase;
  37. static protected $ldapBaseUsers;
  38. static protected $ldapBaseGroups;
  39. static protected $ldapAgentName;
  40. static protected $ldapAgentPassword;
  41. static protected $ldapTLS;
  42. static protected $ldapNoCase;
  43. // user and group settings, that are needed in both backends
  44. static protected $ldapUserDisplayName;
  45. static protected $ldapUserFilter;
  46. static protected $ldapGroupDisplayName;
  47. static protected $ldapLoginFilter;
  48. static public function init() {
  49. self::readConfiguration();
  50. self::establishConnection();
  51. }
  52. static public function destruct() {
  53. @ldap_unbind(self::$ldapConnectionRes);
  54. }
  55. /**
  56. * @brief returns a read-only configuration value
  57. * @param $key the name of the configuration value
  58. * @returns the value on success, otherwise null
  59. *
  60. * returns a read-only configuration values
  61. *
  62. * we cannot work with getters, because it is a static class
  63. */
  64. static public function conf($key) {
  65. if(!self::$configured) {
  66. self::init();
  67. }
  68. $availableProperties = array(
  69. 'ldapUserDisplayName',
  70. 'ldapGroupDisplayName',
  71. 'ldapLoginFilter'
  72. );
  73. if(in_array($key, $availableProperties)) {
  74. return self::$$key;
  75. }
  76. return null;
  77. }
  78. /**
  79. * gives back the database table for the query
  80. */
  81. static private function getMapTable($isUser) {
  82. if($isUser) {
  83. return '*PREFIX*ldap_user_mapping';
  84. } else {
  85. return '*PREFIX*ldap_group_mapping';
  86. }
  87. }
  88. /**
  89. * @brief returns the LDAP DN for the given internal ownCloud name of the group
  90. * @param $name the ownCloud name in question
  91. * @returns string with the LDAP DN on success, otherwise false
  92. *
  93. * returns the LDAP DN for the given internal ownCloud name of the group
  94. */
  95. static public function groupname2dn($name) {
  96. return self::ocname2dn($name, false);
  97. }
  98. /**
  99. * @brief returns the LDAP DN for the given internal ownCloud name of the user
  100. * @param $name the ownCloud name in question
  101. * @returns string with the LDAP DN on success, otherwise false
  102. *
  103. * returns the LDAP DN for the given internal ownCloud name of the user
  104. */
  105. static public function username2dn($name) {
  106. $dn = self::ocname2dn($name, true);
  107. if($dn) {
  108. return $dn;
  109. } else {
  110. //fallback: user is not mapped
  111. self::init();
  112. $filter = self::combineFilterWithAnd(array(
  113. self::$ldapUserFilter,
  114. self::$ldapUserDisplayName . '=' . $name,
  115. ));
  116. $result = self::searchUsers($filter, 'dn');
  117. if(isset($result[0]['dn'])) {
  118. self::mapUser($result[0], $name);
  119. return $result[0];
  120. }
  121. }
  122. return false;
  123. }
  124. static private function ocname2dn($name, $isUser) {
  125. $table = self::getMapTable($isUser);
  126. $query = OCP\DB::prepare('
  127. SELECT ldap_dn
  128. FROM '.$table.'
  129. WHERE owncloud_name = ?
  130. ');
  131. $record = $query->execute(array($name))->fetchOne();
  132. return $record;
  133. if($name=='Coyotes') {
  134. echo("adsfasdf ");
  135. var_dump($record);
  136. die();
  137. }
  138. if(isset($record['ldap_dn'])) {
  139. return $record['ldap_dn'];
  140. }
  141. return false;
  142. }
  143. /**
  144. * @brief returns the internal ownCloud name for the given LDAP DN of the group
  145. * @param $dn the dn of the group object
  146. * @param $ldapname optional, the display name of the object
  147. * @returns string with with the name to use in ownCloud
  148. *
  149. * returns the internal ownCloud name for the given LDAP DN of the group
  150. */
  151. static public function dn2groupname($dn, $ldapname = null) {
  152. return self::dn2ocname($dn, $ldapname, false);
  153. }
  154. /**
  155. * @brief returns the internal ownCloud name for the given LDAP DN of the user
  156. * @param $dn the dn of the user object
  157. * @param $ldapname optional, the display name of the object
  158. * @returns string with with the name to use in ownCloud
  159. *
  160. * returns the internal ownCloud name for the given LDAP DN of the user
  161. */
  162. static public function dn2username($dn, $ldapname = null) {
  163. return self::dn2ocname($dn, $ldapname, true);
  164. }
  165. static public function dn2ocname($dn, $ldapname = null, $isUser = true) {
  166. $dn = self::sanitizeDN($dn);
  167. $table = self::getMapTable($isUser);
  168. if($isUser) {
  169. $nameAttribute = self::conf('ldapUserDisplayName');
  170. } else {
  171. $nameAttribute = self::conf('ldapGroupDisplayName');
  172. }
  173. $query = OCP\DB::prepare('
  174. SELECT owncloud_name
  175. FROM '.$table.'
  176. WHERE ldap_dn = ?
  177. ');
  178. $component = $query->execute(array($dn))->fetchOne();
  179. if($component) {
  180. return $component;
  181. }
  182. if(is_null($ldapname)) {
  183. $ldapname = self::readAttribute($dn, $nameAttribute);
  184. $ldapname = $ldapname[0];
  185. }
  186. //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot.
  187. if(self::mapComponent($dn, $ldapname, $isUser)) {
  188. return $ldapname;
  189. }
  190. //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located.
  191. $oc_name = self::alternateOwnCloudName($ldapname, $dn);
  192. if(self::mapComponent($dn, $oc_name, $isUser)) {
  193. return $oc_name;
  194. }
  195. //and this of course should never been thrown :)
  196. throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
  197. }
  198. /**
  199. * @brief gives back the user names as they are used ownClod internally
  200. * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... )
  201. * @returns an array with the user names to use in ownCloud
  202. *
  203. * gives back the user names as they are used ownClod internally
  204. */
  205. static public function ownCloudUserNames($ldapUsers) {
  206. return self::ldap2ownCloudNames($ldapUsers, true);
  207. }
  208. /**
  209. * @brief gives back the group names as they are used ownClod internally
  210. * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... )
  211. * @returns an array with the group names to use in ownCloud
  212. *
  213. * gives back the group names as they are used ownClod internally
  214. */
  215. static public function ownCloudGroupNames($ldapGroups) {
  216. return self::ldap2ownCloudNames($ldapGroups, false);
  217. }
  218. static private function ldap2ownCloudNames($ldapObjects, $isUsers) {
  219. if($isUsers) {
  220. $knownObjects = self::mappedUsers();
  221. $nameAttribute = self::conf('ldapUserDisplayName');
  222. } else {
  223. $knownObjects = self::mappedGroups();
  224. $nameAttribute = self::conf('ldapGroupDisplayName');
  225. }
  226. $ownCloudNames = array();
  227. foreach($ldapObjects as $ldapObject) {
  228. $key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']);
  229. //everything is fine when we know the group
  230. if($key) {
  231. $ownCloudNames[] = $knownObjects[$key]['owncloud_name'];
  232. continue;
  233. }
  234. //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict
  235. if(self::mapComponent($ldapObject['dn'], $ldapObject[$nameAttribute], $isUsers)) {
  236. $ownCloudNames[] = $ldapObject[$nameAttribute];
  237. continue;
  238. }
  239. //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located.
  240. $oc_name = self::alternateOwnCloudName($ldapObject[$nameAttribute], $ldapObject['dn']);
  241. if(self::mapComponent($ldapObject['dn'], $oc_name, $isUsers)) {
  242. $ownCloudNames[] = $oc_name;
  243. continue;
  244. }
  245. //and this of course should never been thrown :)
  246. throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
  247. }
  248. return $ownCloudNames;
  249. }
  250. /**
  251. * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
  252. * @param $name the display name of the object
  253. * @param $dn the dn of the object
  254. * @returns string with with the name to use in ownCloud
  255. *
  256. * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
  257. */
  258. static private function alternateOwnCloudName($name, $dn) {
  259. $ufn = ldap_dn2ufn($dn);
  260. return $name . ' (' . trim(substr_replace($ufn, '', 0, strpos($ufn, ','))) . ')';
  261. }
  262. /**
  263. * @brief retrieves all known groups from the mappings table
  264. * @returns array with the results
  265. *
  266. * retrieves all known groups from the mappings table
  267. */
  268. static private function mappedGroups() {
  269. return self::mappedComponents(false);
  270. }
  271. /**
  272. * @brief retrieves all known users from the mappings table
  273. * @returns array with the results
  274. *
  275. * retrieves all known users from the mappings table
  276. */
  277. static private function mappedUsers() {
  278. return self::mappedComponents(true);
  279. }
  280. static private function mappedComponents($isUsers) {
  281. $table = self::getMapTable($isUsers);
  282. $query = OCP\DB::prepare('
  283. SELECT ldap_dn, owncloud_name
  284. FROM '. $table
  285. );
  286. return $query->execute()->fetchAll();
  287. }
  288. /**
  289. * @brief inserts a new group into the mappings table
  290. * @param $dn the record in question
  291. * @param $ocname the name to use in ownCloud
  292. * @returns true on success, false otherwise
  293. *
  294. * inserts a new group into the mappings table
  295. */
  296. static private function mapGroup($dn, $ocname) {
  297. return self::mapComponent($dn, $ocname, false);
  298. }
  299. /**
  300. * @brief inserts a new user into the mappings table
  301. * @param $dn the record in question
  302. * @param $ocname the name to use in ownCloud
  303. * @returns true on success, false otherwise
  304. *
  305. * inserts a new user into the mappings table
  306. */
  307. static private function mapUser($dn, $ocname) {
  308. return self::mapComponent($dn, $ocname, true);
  309. }
  310. /**
  311. * @brief inserts a new user or group into the mappings table
  312. * @param $dn the record in question
  313. * @param $ocname the name to use in ownCloud
  314. * @param $isUser is it a user or a group?
  315. * @returns true on success, false otherwise
  316. *
  317. * inserts a new user or group into the mappings table
  318. */
  319. static private function mapComponent($dn, $ocname, $isUser = true) {
  320. $table = self::getMapTable($isUser);
  321. $dn = self::sanitizeDN($dn);
  322. $sqliteAdjustment = '';
  323. $dbtype = OCP\Config::getSystemValue('dbtype');
  324. if(($dbtype == 'sqlite') || ($dbtype == 'sqlite3')) {
  325. $sqliteAdjustment = 'OR';
  326. }
  327. $insert = OCP\DB::prepare('
  328. INSERT '.$sqliteAdjustment.' IGNORE INTO '.$table.'
  329. (ldap_dn, owncloud_name)
  330. VALUES (?,?)
  331. ');
  332. $res = $insert->execute(array($dn, $ocname));
  333. return !OCP\DB::isError($res);
  334. }
  335. static public function fetchListOfUsers($filter, $attr) {
  336. return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1));
  337. }
  338. static public function fetchListOfGroups($filter, $attr) {
  339. return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1));
  340. }
  341. static private function fetchList($list, $manyAttributes) {
  342. if(is_array($list)) {
  343. if($manyAttributes) {
  344. return $list;
  345. } else {
  346. return array_unique($list, SORT_LOCALE_STRING);
  347. }
  348. }
  349. //error cause actually, maybe throw an exception in future.
  350. return array();
  351. }
  352. /**
  353. * @brief reads a given attribute for an LDAP record identified by a DN
  354. * @param $dn the record in question
  355. * @param $attr the attribute that shall be retrieved
  356. * @returns the values in an array on success, false otherwise
  357. *
  358. * Reads an attribute from an LDAP entry
  359. */
  360. static public function readAttribute($dn, $attr) {
  361. $cr = self::getConnectionResource();
  362. $rr = ldap_read($cr, $dn, 'objectClass=*', array($attr));
  363. $er = ldap_first_entry($cr, $rr);
  364. //LDAP attributes are not case sensitive
  365. $result = array_change_key_case(ldap_get_attributes($cr, $er));
  366. $attr = strtolower($attr);
  367. if(isset($result[$attr]) && $result[$attr]['count'] > 0){
  368. $values = array();
  369. for($i=0;$i<$result[$attr]['count'];$i++) {
  370. $values[] = $result[$attr][$i];
  371. }
  372. return $values;
  373. }
  374. return false;
  375. }
  376. /**
  377. * @brief executes an LDAP search, optimized for Users
  378. * @param $filter the LDAP filter for the search
  379. * @param $attr optional, when a certain attribute shall be filtered out
  380. * @returns array with the search result
  381. *
  382. * Executes an LDAP search
  383. */
  384. static public function searchUsers($filter, $attr = null) {
  385. self::init();
  386. return self::search($filter, self::$ldapBaseUsers, $attr);
  387. }
  388. /**
  389. * @brief executes an LDAP search, optimized for Groups
  390. * @param $filter the LDAP filter for the search
  391. * @param $attr optional, when a certain attribute shall be filtered out
  392. * @returns array with the search result
  393. *
  394. * Executes an LDAP search
  395. */
  396. static public function searchGroups($filter, $attr = null) {
  397. self::init();
  398. return self::search($filter, self::$ldapBaseGroups, $attr);
  399. }
  400. /**
  401. * @brief executes an LDAP search
  402. * @param $filter the LDAP filter for the search
  403. * @param $base the LDAP subtree that shall be searched
  404. * @param $attr optional, when a certain attribute shall be filtered out
  405. * @returns array with the search result
  406. *
  407. * Executes an LDAP search
  408. */
  409. static private function search($filter, $base, $attr = null) {
  410. if(!is_null($attr) && !is_array($attr)) {
  411. $attr = array(strtolower($attr));
  412. }
  413. $sr = @ldap_search(self::getConnectionResource(), $base, $filter, $attr);
  414. $findings = @ldap_get_entries(self::getConnectionResource(), $sr );
  415. // if we're here, probably no connection ressource is returned.
  416. // to make ownCloud behave nicely, we simply give back an empty array.
  417. if(is_null($findings)) {
  418. return array();
  419. }
  420. if(!is_null($attr)) {
  421. $selection = array();
  422. $multiarray = false;
  423. if(count($attr) > 1) {
  424. $multiarray = true;
  425. $i = 0;
  426. }
  427. foreach($findings as $item) {
  428. if($multiarray) {
  429. foreach($attr as $key) {
  430. if(isset($item[$key])) {
  431. if($key != 'dn'){
  432. $selection[$i][$key] = $item[$key][0];
  433. } else {
  434. $selection[$i][$key] = self::sanitizeDN($item[$key]);
  435. }
  436. }
  437. }
  438. $i++;
  439. } else {
  440. //tribute to case insensitivity
  441. if(!is_array($item)) {
  442. continue;
  443. }
  444. $item = array_change_key_case($item);
  445. $key = strtolower($attr[0]);
  446. if(isset($item[$key])) {
  447. if($key == 'dn') {
  448. $selection[] = self::sanitizeDN($item[$key]);
  449. } else {
  450. $selection[] = $item[$key];
  451. }
  452. }
  453. }
  454. }
  455. return $selection;
  456. }
  457. return $findings;
  458. }
  459. static private function sanitizeDN($dn) {
  460. //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this!
  461. $dn = preg_replace('/,\s+/',',',$dn);
  462. //make comparisons and everything work
  463. $dn = strtolower($dn);
  464. return $dn;
  465. }
  466. /**
  467. * @brief combines the input filters with AND
  468. * @param $filters array, the filters to connect
  469. * @returns the combined filter
  470. *
  471. * Combines Filter arguments with AND
  472. */
  473. static public function combineFilterWithAnd($filters) {
  474. return self::combineFilter($filters,'&');
  475. }
  476. /**
  477. * @brief combines the input filters with AND
  478. * @param $filters array, the filters to connect
  479. * @returns the combined filter
  480. *
  481. * Combines Filter arguments with AND
  482. */
  483. static public function combineFilterWithOr($filters) {
  484. return self::combineFilter($filters,'|');
  485. }
  486. /**
  487. * @brief combines the input filters with given operator
  488. * @param $filters array, the filters to connect
  489. * @param $operator either & or |
  490. * @returns the combined filter
  491. *
  492. * Combines Filter arguments with AND
  493. */
  494. static private function combineFilter($filters, $operator) {
  495. $combinedFilter = '('.$operator;
  496. foreach($filters as $filter) {
  497. if(substr($filter,0,1) != '(') {
  498. $filter = '('.$filter.')';
  499. }
  500. $combinedFilter.=$filter;
  501. }
  502. $combinedFilter.=')';
  503. return $combinedFilter;
  504. }
  505. /**
  506. * Returns the LDAP handler
  507. */
  508. static private function getConnectionResource() {
  509. if(!self::$ldapConnectionRes) {
  510. self::init();
  511. }
  512. if(is_null(self::$ldapConnectionRes)) {
  513. OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO);
  514. }
  515. return self::$ldapConnectionRes;
  516. }
  517. /**
  518. * Caches the general LDAP configuration.
  519. */
  520. static private function readConfiguration() {
  521. if(!self::$configured) {
  522. self::$ldapHost = OCP\Config::getAppValue('user_ldap', 'ldap_host', '');
  523. self::$ldapPort = OCP\Config::getAppValue('user_ldap', 'ldap_port', OC_USER_BACKEND_LDAP_DEFAULT_PORT);
  524. self::$ldapAgentName = OCP\Config::getAppValue('user_ldap', 'ldap_dn','');
  525. self::$ldapAgentPassword = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password',''));
  526. self::$ldapBase = OCP\Config::getAppValue('user_ldap', 'ldap_base', '');
  527. self::$ldapBaseUsers = OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase);
  528. self::$ldapBaseGroups = OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase);
  529. self::$ldapTLS = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0);
  530. self::$ldapNoCase = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0);
  531. self::$ldapUserDisplayName = OCP\Config::getAppValue('user_ldap', 'ldap_display_name', OC_USER_BACKEND_LDAP_DEFAULT_DISPLAY_NAME);
  532. self::$ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person');
  533. self::$ldapLoginFilter = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)');
  534. self::$ldapGroupDisplayName = OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR);
  535. if(empty(self::$ldapBaseUsers)) {
  536. OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO);
  537. self::$ldapBaseUsers = self::$ldapBase;
  538. }
  539. if(empty(self::$ldapBaseGroups)) {
  540. OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO);
  541. self::$ldapBaseGroups = self::$ldapBase;
  542. }
  543. if(
  544. !empty(self::$ldapHost)
  545. && !empty(self::$ldapPort)
  546. && (
  547. (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword))
  548. || ( empty(self::$ldapAgentName) && empty(self::$ldapAgentPassword))
  549. )
  550. && !empty(self::$ldapBase)
  551. && !empty(self::$ldapUserDisplayName)
  552. )
  553. {
  554. self::$configured = true;
  555. }
  556. }
  557. }
  558. /**
  559. * Connects and Binds to LDAP
  560. */
  561. static private function establishConnection() {
  562. if(!self::$configured) {
  563. OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO);
  564. return false;
  565. }
  566. if(!self::$ldapConnectionRes) {
  567. self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort);
  568. if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
  569. if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
  570. if(self::$ldapTLS) {
  571. ldap_start_tls(self::$ldapConnectionRes);
  572. }
  573. }
  574. }
  575. $ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword );
  576. if(!$ldapLogin) {
  577. OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR);
  578. return false;
  579. }
  580. }
  581. }
  582. static public function areCredentialsValid($name, $password) {
  583. return @ldap_bind(self::getConnectionResource(), $name, $password);
  584. }
  585. /**
  586. * taken from http://www.php.net/manual/en/function.array-search.php#97645
  587. * TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe?
  588. */
  589. static public function recursiveArraySearch($haystack, $needle, $index = null) {
  590. $aIt = new RecursiveArrayIterator($haystack);
  591. $it = new RecursiveIteratorIterator($aIt);
  592. while($it->valid()) {
  593. if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
  594. return $aIt->key();
  595. }
  596. $it->next();
  597. }
  598. return false;
  599. }
  600. }