WizardTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Thomas Müller <thomas.mueller@tmit.eu>
  9. * @author Viktor Szépe <viktor@szepe.net>
  10. *
  11. * @license AGPL-3.0
  12. *
  13. * This code is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License, version 3,
  15. * as published by the Free Software Foundation.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License, version 3,
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>
  24. *
  25. */
  26. namespace OCA\User_LDAP\Tests;
  27. use OCA\User_LDAP\ILDAPWrapper;
  28. use \OCA\User_LDAP\Wizard;
  29. /**
  30. * Class Test_Wizard
  31. *
  32. * @group DB
  33. *
  34. * @package OCA\User_LDAP\Tests
  35. */
  36. class WizardTest extends \Test\TestCase {
  37. protected function setUp() {
  38. parent::setUp();
  39. //we need to make sure the consts are defined, otherwise tests will fail
  40. //on systems without php5_ldap
  41. $ldapConsts = array('LDAP_OPT_PROTOCOL_VERSION',
  42. 'LDAP_OPT_REFERRALS', 'LDAP_OPT_NETWORK_TIMEOUT');
  43. foreach($ldapConsts as $const) {
  44. if(!defined($const)) {
  45. define($const, 42);
  46. }
  47. }
  48. }
  49. private function getWizardAndMocks() {
  50. static $confMethods;
  51. static $connMethods;
  52. static $accMethods;
  53. if(is_null($confMethods)) {
  54. $confMethods = get_class_methods('\OCA\User_LDAP\Configuration');
  55. $connMethods = get_class_methods('\OCA\User_LDAP\Connection');
  56. $accMethods = get_class_methods('\OCA\User_LDAP\Access');
  57. }
  58. $lw = $this->createMock(ILDAPWrapper::class);
  59. $conf = $this->getMockBuilder('\OCA\User_LDAP\Configuration')
  60. ->setMethods($confMethods)
  61. ->setConstructorArgs([$lw, null, null])
  62. ->getMock();
  63. $connector = $this->getMockBuilder('\OCA\User_LDAP\Connection')
  64. ->setMethods($connMethods)
  65. ->setConstructorArgs([$lw, null, null])
  66. ->getMock();
  67. $um = $this->getMockBuilder('\OCA\User_LDAP\User\Manager')
  68. ->disableOriginalConstructor()
  69. ->getMock();
  70. $helper = new \OCA\User_LDAP\Helper(\OC::$server->getConfig());
  71. $access = $this->getMockBuilder('\OCA\User_LDAP\Access')
  72. ->setMethods($accMethods)
  73. ->setConstructorArgs([$connector, $lw, $um, $helper])
  74. ->getMock();
  75. return array(new Wizard($conf, $lw, $access), $conf, $lw, $access);
  76. }
  77. private function prepareLdapWrapperForConnections(&$ldap) {
  78. $ldap->expects($this->once())
  79. ->method('connect')
  80. //dummy value, usually invalid
  81. ->will($this->returnValue(true));
  82. $ldap->expects($this->exactly(3))
  83. ->method('setOption')
  84. ->will($this->returnValue(true));
  85. $ldap->expects($this->once())
  86. ->method('bind')
  87. ->will($this->returnValue(true));
  88. }
  89. public function testCumulativeSearchOnAttributeLimited() {
  90. list($wizard, $configuration, $ldap) = $this->getWizardAndMocks();
  91. $configuration->expects($this->any())
  92. ->method('__get')
  93. ->will($this->returnCallback(function($name) {
  94. if($name === 'ldapBase') {
  95. return array('base');
  96. }
  97. return null;
  98. }));
  99. $this->prepareLdapWrapperForConnections($ldap);
  100. $ldap->expects($this->any())
  101. ->method('isResource')
  102. ->will($this->returnValue(true));
  103. $ldap->expects($this->exactly(2))
  104. ->method('search')
  105. //dummy value, usually invalid
  106. ->will($this->returnValue(true));
  107. $ldap->expects($this->exactly(2))
  108. ->method('countEntries')
  109. //an is_resource check will follow, so we need to return a dummy resource
  110. ->will($this->returnValue(23));
  111. //5 DNs per filter means 2x firstEntry and 8x nextEntry
  112. $ldap->expects($this->exactly(2))
  113. ->method('firstEntry')
  114. //dummy value, usually invalid
  115. ->will($this->returnValue(true));
  116. $ldap->expects($this->exactly(8))
  117. ->method('nextEntry')
  118. //dummy value, usually invalid
  119. ->will($this->returnValue(true));
  120. $ldap->expects($this->exactly(10))
  121. ->method('getAttributes')
  122. //dummy value, usually invalid
  123. ->will($this->returnValue(array('cn' => array('foo'), 'count' => 1)));
  124. global $uidnumber;
  125. $uidnumber = 1;
  126. $ldap->expects($this->exactly(10))
  127. ->method('getDN')
  128. //dummy value, usually invalid
  129. ->will($this->returnCallback(function($a, $b) {
  130. global $uidnumber;
  131. return $uidnumber++;
  132. }));
  133. // The following expectations are the real test
  134. $filters = array('f1', 'f2', '*');
  135. $wizard->cumulativeSearchOnAttribute($filters, 'cn', 5);
  136. unset($uidnumber);
  137. }
  138. public function testCumulativeSearchOnAttributeUnlimited() {
  139. list($wizard, $configuration, $ldap) = $this->getWizardAndMocks();
  140. $configuration->expects($this->any())
  141. ->method('__get')
  142. ->will($this->returnCallback(function($name) {
  143. if($name === 'ldapBase') {
  144. return array('base');
  145. }
  146. return null;
  147. }));
  148. $this->prepareLdapWrapperForConnections($ldap);
  149. $ldap->expects($this->any())
  150. ->method('isResource')
  151. ->will($this->returnCallback(function($r) {
  152. if($r === true) {
  153. return true;
  154. }
  155. if($r % 24 === 0) {
  156. global $uidnumber;
  157. $uidnumber++;
  158. return false;
  159. }
  160. return true;
  161. }));
  162. $ldap->expects($this->exactly(2))
  163. ->method('search')
  164. //dummy value, usually invalid
  165. ->will($this->returnValue(true));
  166. $ldap->expects($this->exactly(2))
  167. ->method('countEntries')
  168. //an is_resource check will follow, so we need to return a dummy resource
  169. ->will($this->returnValue(23));
  170. //5 DNs per filter means 2x firstEntry and 8x nextEntry
  171. $ldap->expects($this->exactly(2))
  172. ->method('firstEntry')
  173. //dummy value, usually invalid
  174. ->will($this->returnCallback(function($r) {
  175. global $uidnumber;
  176. return $uidnumber;
  177. }));
  178. $ldap->expects($this->exactly(46))
  179. ->method('nextEntry')
  180. //dummy value, usually invalid
  181. ->will($this->returnCallback(function($r) {
  182. global $uidnumber;
  183. return $uidnumber;
  184. }));
  185. $ldap->expects($this->exactly(46))
  186. ->method('getAttributes')
  187. //dummy value, usually invalid
  188. ->will($this->returnValue(array('cn' => array('foo'), 'count' => 1)));
  189. global $uidnumber;
  190. $uidnumber = 1;
  191. $ldap->expects($this->exactly(46))
  192. ->method('getDN')
  193. //dummy value, usually invalid
  194. ->will($this->returnCallback(function($a, $b) {
  195. global $uidnumber;
  196. return $uidnumber++;
  197. }));
  198. // The following expectations are the real test
  199. $filters = array('f1', 'f2', '*');
  200. $wizard->cumulativeSearchOnAttribute($filters, 'cn', 0);
  201. unset($uidnumber);
  202. }
  203. public function testDetectEmailAttributeAlreadySet() {
  204. list($wizard, $configuration, $ldap, $access)
  205. = $this->getWizardAndMocks();
  206. $configuration->expects($this->any())
  207. ->method('__get')
  208. ->will($this->returnCallback(function ($name) {
  209. if($name === 'ldapEmailAttribute') {
  210. return 'myEmailAttribute';
  211. } else {
  212. //for requirement checks
  213. return 'let me pass';
  214. }
  215. }));
  216. $access->expects($this->once())
  217. ->method('countUsers')
  218. ->will($this->returnValue(42));
  219. $wizard->detectEmailAttribute();
  220. }
  221. public function testDetectEmailAttributeOverrideSet() {
  222. list($wizard, $configuration, $ldap, $access)
  223. = $this->getWizardAndMocks();
  224. $configuration->expects($this->any())
  225. ->method('__get')
  226. ->will($this->returnCallback(function ($name) {
  227. if($name === 'ldapEmailAttribute') {
  228. return 'myEmailAttribute';
  229. } else {
  230. //for requirement checks
  231. return 'let me pass';
  232. }
  233. }));
  234. $access->expects($this->exactly(3))
  235. ->method('combineFilterWithAnd')
  236. ->will($this->returnCallback(function ($filterParts) {
  237. return str_replace('=*', '', array_pop($filterParts));
  238. }));
  239. $access->expects($this->exactly(3))
  240. ->method('countUsers')
  241. ->will($this->returnCallback(function ($filter) {
  242. if($filter === 'myEmailAttribute') {
  243. return 0;
  244. } else if($filter === 'mail') {
  245. return 3;
  246. } else if($filter === 'mailPrimaryAddress') {
  247. return 17;
  248. }
  249. throw new \Exception('Untested filter: ' . $filter);
  250. }));
  251. $result = $wizard->detectEmailAttribute()->getResultArray();
  252. $this->assertSame('mailPrimaryAddress',
  253. $result['changes']['ldap_email_attr']);
  254. }
  255. public function testDetectEmailAttributeFind() {
  256. list($wizard, $configuration, $ldap, $access)
  257. = $this->getWizardAndMocks();
  258. $configuration->expects($this->any())
  259. ->method('__get')
  260. ->will($this->returnCallback(function ($name) {
  261. if($name === 'ldapEmailAttribute') {
  262. return '';
  263. } else {
  264. //for requirement checks
  265. return 'let me pass';
  266. }
  267. }));
  268. $access->expects($this->exactly(2))
  269. ->method('combineFilterWithAnd')
  270. ->will($this->returnCallback(function ($filterParts) {
  271. return str_replace('=*', '', array_pop($filterParts));
  272. }));
  273. $access->expects($this->exactly(2))
  274. ->method('countUsers')
  275. ->will($this->returnCallback(function ($filter) {
  276. if($filter === 'myEmailAttribute') {
  277. return 0;
  278. } else if($filter === 'mail') {
  279. return 3;
  280. } else if($filter === 'mailPrimaryAddress') {
  281. return 17;
  282. }
  283. throw new \Exception('Untested filter: ' . $filter);
  284. }));
  285. $result = $wizard->detectEmailAttribute()->getResultArray();
  286. $this->assertSame('mailPrimaryAddress',
  287. $result['changes']['ldap_email_attr']);
  288. }
  289. public function testDetectEmailAttributeFindNothing() {
  290. list($wizard, $configuration, $ldap, $access)
  291. = $this->getWizardAndMocks();
  292. $configuration->expects($this->any())
  293. ->method('__get')
  294. ->will($this->returnCallback(function ($name) {
  295. if($name === 'ldapEmailAttribute') {
  296. return 'myEmailAttribute';
  297. } else {
  298. //for requirement checks
  299. return 'let me pass';
  300. }
  301. }));
  302. $access->expects($this->exactly(3))
  303. ->method('combineFilterWithAnd')
  304. ->will($this->returnCallback(function ($filterParts) {
  305. return str_replace('=*', '', array_pop($filterParts));
  306. }));
  307. $access->expects($this->exactly(3))
  308. ->method('countUsers')
  309. ->will($this->returnCallback(function ($filter) {
  310. if($filter === 'myEmailAttribute') {
  311. return 0;
  312. } else if($filter === 'mail') {
  313. return 0;
  314. } else if($filter === 'mailPrimaryAddress') {
  315. return 0;
  316. }
  317. throw new \Exception('Untested filter: ' . $filter);
  318. }));
  319. $result = $wizard->detectEmailAttribute();
  320. $this->assertSame(false, $result->hasChanges());
  321. }
  322. public function testCumulativeSearchOnAttributeSkipReadDN() {
  323. // tests that there is no infinite loop, when skipping already processed
  324. // DNs (they can be returned multiple times for multiple filters )
  325. list($wizard, $configuration, $ldap) = $this->getWizardAndMocks();
  326. $configuration->expects($this->any())
  327. ->method('__get')
  328. ->will($this->returnCallback(function($name) {
  329. if($name === 'ldapBase') {
  330. return array('base');
  331. }
  332. return null;
  333. }));
  334. $this->prepareLdapWrapperForConnections($ldap);
  335. $ldap->expects($this->any())
  336. ->method('isResource')
  337. ->will($this->returnCallback(function($res) {
  338. return (bool)$res;
  339. }));
  340. $ldap->expects($this->any())
  341. ->method('search')
  342. //dummy value, usually invalid
  343. ->will($this->returnValue(true));
  344. $ldap->expects($this->any())
  345. ->method('countEntries')
  346. //an is_resource check will follow, so we need to return a dummy resource
  347. ->will($this->returnValue(7));
  348. //5 DNs per filter means 2x firstEntry and 8x nextEntry
  349. $ldap->expects($this->any())
  350. ->method('firstEntry')
  351. //dummy value, usually invalid
  352. ->will($this->returnValue(1));
  353. global $mark;
  354. $mark = false;
  355. // entries return order: 1, 2, 3, 4, 4, 5, 6
  356. $ldap->expects($this->any())
  357. ->method('nextEntry')
  358. //dummy value, usually invalid
  359. ->will($this->returnCallback(function($a, $prev){
  360. $current = $prev + 1;
  361. if($current === 7) {
  362. return false;
  363. }
  364. global $mark;
  365. if($prev === 4 && !$mark) {
  366. $mark = true;
  367. return 4;
  368. }
  369. return $current;
  370. }));
  371. $ldap->expects($this->any())
  372. ->method('getAttributes')
  373. //dummy value, usually invalid
  374. ->will($this->returnCallback(function($a, $entry) {
  375. return array('cn' => array($entry), 'count' => 1);
  376. }));
  377. $ldap->expects($this->any())
  378. ->method('getDN')
  379. //dummy value, usually invalid
  380. ->will($this->returnCallback(function($a, $b) {
  381. return $b;
  382. }));
  383. // The following expectations are the real test
  384. $filters = array('f1', 'f2', '*');
  385. $resultArray = $wizard->cumulativeSearchOnAttribute($filters, 'cn', 0);
  386. $this->assertSame(6, count($resultArray));
  387. unset($mark);
  388. }
  389. }