UserTest.php 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\User_LDAP\Tests\User;
  8. use OCA\User_LDAP\Access;
  9. use OCA\User_LDAP\Connection;
  10. use OCA\User_LDAP\FilesystemHelper;
  11. use OCA\User_LDAP\User\User;
  12. use OCP\IAvatar;
  13. use OCP\IAvatarManager;
  14. use OCP\IConfig;
  15. use OCP\Image;
  16. use OCP\IUser;
  17. use OCP\IUserManager;
  18. use OCP\Notification\IManager as INotificationManager;
  19. use OCP\Notification\INotification;
  20. use Psr\Log\LoggerInterface;
  21. /**
  22. * Class UserTest
  23. *
  24. * @group DB
  25. *
  26. * @package OCA\User_LDAP\Tests\User
  27. */
  28. class UserTest extends \Test\TestCase {
  29. /** @var Access|\PHPUnit\Framework\MockObject\MockObject */
  30. protected $access;
  31. /** @var Connection|\PHPUnit\Framework\MockObject\MockObject */
  32. protected $connection;
  33. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  34. protected $config;
  35. /** @var FilesystemHelper|\PHPUnit\Framework\MockObject\MockObject */
  36. protected $filesystemhelper;
  37. /** @var INotificationManager|\PHPUnit\Framework\MockObject\MockObject */
  38. protected $notificationManager;
  39. /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
  40. protected $userManager;
  41. /** @var Image|\PHPUnit\Framework\MockObject\MockObject */
  42. protected $image;
  43. /** @var IAvatarManager|\PHPUnit\Framework\MockObject\MockObject */
  44. protected $avatarManager;
  45. /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
  46. protected $logger;
  47. /** @var string */
  48. protected $uid = 'alice';
  49. /** @var string */
  50. protected $dn = 'uid=alice,dc=foo,dc=bar';
  51. /** @var User */
  52. protected $user;
  53. protected function setUp(): void {
  54. parent::setUp();
  55. $this->connection = $this->createMock(Connection::class);
  56. $this->access = $this->createMock(Access::class);
  57. $this->access->connection = $this->connection;
  58. $this->access->expects($this->any())
  59. ->method('getConnection')
  60. ->willReturn($this->connection);
  61. $this->config = $this->createMock(IConfig::class);
  62. $this->filesystemhelper = $this->createMock(FilesystemHelper::class);
  63. $this->logger = $this->createMock(LoggerInterface::class);
  64. $this->avatarManager = $this->createMock(IAvatarManager::class);
  65. $this->image = $this->createMock(Image::class);
  66. $this->userManager = $this->createMock(IUserManager::class);
  67. $this->notificationManager = $this->createMock(INotificationManager::class);
  68. $this->user = new User(
  69. $this->uid,
  70. $this->dn,
  71. $this->access,
  72. $this->config,
  73. $this->filesystemhelper,
  74. $this->image,
  75. $this->logger,
  76. $this->avatarManager,
  77. $this->userManager,
  78. $this->notificationManager
  79. );
  80. }
  81. public function testGetDNandUsername(): void {
  82. $this->assertSame($this->dn, $this->user->getDN());
  83. $this->assertSame($this->uid, $this->user->getUsername());
  84. }
  85. public function testUpdateEmailProvided(): void {
  86. $this->connection->expects($this->once())
  87. ->method('__get')
  88. ->with($this->equalTo('ldapEmailAttribute'))
  89. ->willReturn('email');
  90. $this->access->expects($this->once())
  91. ->method('readAttribute')
  92. ->with($this->equalTo($this->dn),
  93. $this->equalTo('email'))
  94. ->willReturn(['alice@foo.bar']);
  95. $coreUser = $this->getMockBuilder(IUser::class)
  96. ->disableOriginalConstructor()
  97. ->getMock();
  98. $coreUser->expects($this->once())
  99. ->method('setEMailAddress')
  100. ->with('alice@foo.bar');
  101. $this->userManager->expects($this->any())
  102. ->method('get')
  103. ->willReturn($coreUser);
  104. $this->user->updateEmail();
  105. }
  106. public function testUpdateEmailNotProvided(): void {
  107. $this->connection->expects($this->once())
  108. ->method('__get')
  109. ->with($this->equalTo('ldapEmailAttribute'))
  110. ->willReturn('email');
  111. $this->access->expects($this->once())
  112. ->method('readAttribute')
  113. ->with($this->equalTo($this->dn),
  114. $this->equalTo('email'))
  115. ->willReturn(false);
  116. $this->config->expects($this->never())
  117. ->method('setUserValue');
  118. $this->user->updateEmail();
  119. }
  120. public function testUpdateEmailNotConfigured(): void {
  121. $this->connection->expects($this->once())
  122. ->method('__get')
  123. ->with($this->equalTo('ldapEmailAttribute'))
  124. ->willReturn('');
  125. $this->access->expects($this->never())
  126. ->method('readAttribute');
  127. $this->config->expects($this->never())
  128. ->method('setUserValue');
  129. $this->user->updateEmail();
  130. }
  131. public function testUpdateQuotaAllProvided(): void {
  132. $this->connection->expects($this->exactly(2))
  133. ->method('__get')
  134. ->willReturnMap([
  135. ['ldapQuotaAttribute', 'myquota'],
  136. ['ldapQuotaDefault', '']
  137. ]);
  138. $this->access->expects($this->once())
  139. ->method('readAttribute')
  140. ->with($this->equalTo($this->dn),
  141. $this->equalTo('myquota'))
  142. ->willReturn(['42 GB']);
  143. $coreUser = $this->createMock(IUser::class);
  144. $coreUser->expects($this->once())
  145. ->method('setQuota')
  146. ->with('42 GB');
  147. $this->userManager->expects($this->atLeastOnce())
  148. ->method('get')
  149. ->with($this->uid)
  150. ->willReturn($coreUser);
  151. $this->user->updateQuota();
  152. }
  153. public function testUpdateQuotaToDefaultAllProvided(): void {
  154. $this->connection->expects($this->exactly(2))
  155. ->method('__get')
  156. ->willReturnMap([
  157. ['ldapQuotaAttribute', 'myquota'],
  158. ['ldapQuotaDefault', '']
  159. ]);
  160. $this->access->expects($this->once())
  161. ->method('readAttribute')
  162. ->with($this->equalTo($this->dn),
  163. $this->equalTo('myquota'))
  164. ->willReturn(['default']);
  165. $coreUser = $this->createMock(IUser::class);
  166. $coreUser->expects($this->once())
  167. ->method('setQuota')
  168. ->with('default');
  169. $this->userManager->expects($this->once())
  170. ->method('get')
  171. ->with($this->uid)
  172. ->willReturn($coreUser);
  173. $this->user->updateQuota();
  174. }
  175. public function testUpdateQuotaToNoneAllProvided(): void {
  176. $this->connection->expects($this->exactly(2))
  177. ->method('__get')
  178. ->willReturnMap([
  179. ['ldapQuotaAttribute', 'myquota'],
  180. ['ldapQuotaDefault', '']
  181. ]);
  182. $this->access->expects($this->once())
  183. ->method('readAttribute')
  184. ->with($this->equalTo($this->dn),
  185. $this->equalTo('myquota'))
  186. ->willReturn(['none']);
  187. $coreUser = $this->createMock(IUser::class);
  188. $coreUser->expects($this->once())
  189. ->method('setQuota')
  190. ->with('none');
  191. $this->userManager->expects($this->once())
  192. ->method('get')
  193. ->with($this->uid)
  194. ->willReturn($coreUser);
  195. $this->user->updateQuota();
  196. }
  197. public function testUpdateQuotaDefaultProvided(): void {
  198. $this->connection->expects($this->exactly(2))
  199. ->method('__get')
  200. ->willReturnMap([
  201. ['ldapQuotaAttribute', 'myquota'],
  202. ['ldapQuotaDefault', '25 GB'],
  203. ]);
  204. $this->access->expects($this->once())
  205. ->method('readAttribute')
  206. ->with($this->equalTo($this->dn),
  207. $this->equalTo('myquota'))
  208. ->willReturn(false);
  209. $coreUser = $this->createMock(IUser::class);
  210. $coreUser->expects($this->once())
  211. ->method('setQuota')
  212. ->with('25 GB');
  213. $this->userManager->expects($this->once())
  214. ->method('get')
  215. ->with($this->uid)
  216. ->willReturn($coreUser);
  217. $this->user->updateQuota();
  218. }
  219. public function testUpdateQuotaIndividualProvided(): void {
  220. $this->connection->expects($this->exactly(2))
  221. ->method('__get')
  222. ->willReturnMap([
  223. ['ldapQuotaAttribute', 'myquota'],
  224. ['ldapQuotaDefault', '']
  225. ]);
  226. $this->access->expects($this->once())
  227. ->method('readAttribute')
  228. ->with($this->equalTo($this->dn),
  229. $this->equalTo('myquota'))
  230. ->willReturn(['27 GB']);
  231. $coreUser = $this->createMock(IUser::class);
  232. $coreUser->expects($this->once())
  233. ->method('setQuota')
  234. ->with('27 GB');
  235. $this->userManager->expects($this->once())
  236. ->method('get')
  237. ->with($this->uid)
  238. ->willReturn($coreUser);
  239. $this->user->updateQuota();
  240. }
  241. public function testUpdateQuotaNoneProvided(): void {
  242. $this->connection->expects($this->exactly(2))
  243. ->method('__get')
  244. ->willReturnMap([
  245. ['ldapQuotaAttribute', 'myquota'],
  246. ['ldapQuotaDefault', '']
  247. ]);
  248. $this->access->expects($this->once())
  249. ->method('readAttribute')
  250. ->with($this->equalTo($this->dn),
  251. $this->equalTo('myquota'))
  252. ->willReturn(false);
  253. $coreUser = $this->createMock(IUser::class);
  254. $coreUser->expects($this->never())
  255. ->method('setQuota');
  256. $this->userManager->expects($this->never())
  257. ->method('get')
  258. ->with($this->uid);
  259. $this->config->expects($this->never())
  260. ->method('setUserValue');
  261. $this->user->updateQuota();
  262. }
  263. public function testUpdateQuotaNoneConfigured(): void {
  264. $this->connection->expects($this->exactly(2))
  265. ->method('__get')
  266. ->willReturnMap([
  267. ['ldapQuotaAttribute', ''],
  268. ['ldapQuotaDefault', '']
  269. ]);
  270. $coreUser = $this->createMock(IUser::class);
  271. $coreUser->expects($this->never())
  272. ->method('setQuota');
  273. $this->userManager->expects($this->never())
  274. ->method('get');
  275. $this->access->expects($this->never())
  276. ->method('readAttribute');
  277. $this->config->expects($this->never())
  278. ->method('setUserValue');
  279. $this->user->updateQuota();
  280. }
  281. public function testUpdateQuotaFromValue(): void {
  282. $readQuota = '19 GB';
  283. $this->connection->expects($this->exactly(2))
  284. ->method('__get')
  285. ->willReturnMap([
  286. ['ldapQuotaAttribute', 'myquota'],
  287. ['ldapQuotaDefault', '']
  288. ]);
  289. $this->access->expects($this->never())
  290. ->method('readAttribute');
  291. $user = $this->createMock(IUser::class);
  292. $user->expects($this->once())
  293. ->method('setQuota')
  294. ->with($readQuota);
  295. $this->userManager->expects($this->once())
  296. ->method('get')
  297. ->with($this->uid)
  298. ->willReturn($user);
  299. $this->user->updateQuota($readQuota);
  300. }
  301. /**
  302. * Unparseable quota will fallback to use the LDAP default
  303. */
  304. public function testUpdateWrongQuotaAllProvided(): void {
  305. $this->connection->expects($this->exactly(2))
  306. ->method('__get')
  307. ->willReturnMap([
  308. ['ldapQuotaAttribute', 'myquota'],
  309. ['ldapQuotaDefault', '23 GB']
  310. ]);
  311. $this->access->expects($this->once())
  312. ->method('readAttribute')
  313. ->with($this->equalTo($this->dn),
  314. $this->equalTo('myquota'))
  315. ->willReturn(['42 GBwos']);
  316. $coreUser = $this->createMock(IUser::class);
  317. $coreUser->expects($this->once())
  318. ->method('setQuota')
  319. ->with('23 GB');
  320. $this->userManager->expects($this->once())
  321. ->method('get')
  322. ->with($this->uid)
  323. ->willReturn($coreUser);
  324. $this->user->updateQuota();
  325. }
  326. /**
  327. * No user quota and wrong default will set 'default' as quota
  328. */
  329. public function testUpdateWrongDefaultQuotaProvided(): void {
  330. $this->connection->expects($this->exactly(2))
  331. ->method('__get')
  332. ->willReturnMap([
  333. ['ldapQuotaAttribute', 'myquota'],
  334. ['ldapQuotaDefault', '23 GBwowowo']
  335. ]);
  336. $this->access->expects($this->once())
  337. ->method('readAttribute')
  338. ->with($this->equalTo($this->dn),
  339. $this->equalTo('myquota'))
  340. ->willReturn(false);
  341. $coreUser = $this->createMock(IUser::class);
  342. $coreUser->expects($this->never())
  343. ->method('setQuota');
  344. $this->userManager->expects($this->never())
  345. ->method('get');
  346. $this->user->updateQuota();
  347. }
  348. /**
  349. * Wrong user quota and wrong default will set 'default' as quota
  350. */
  351. public function testUpdateWrongQuotaAndDefaultAllProvided(): void {
  352. $this->connection->expects($this->exactly(2))
  353. ->method('__get')
  354. ->willReturnMap([
  355. ['ldapQuotaAttribute', 'myquota'],
  356. ['ldapQuotaDefault', '23 GBwowowo']
  357. ]);
  358. $this->access->expects($this->once())
  359. ->method('readAttribute')
  360. ->with($this->equalTo($this->dn),
  361. $this->equalTo('myquota'))
  362. ->willReturn(['23 flush']);
  363. $coreUser = $this->createMock(IUser::class);
  364. $coreUser->expects($this->never())
  365. ->method('setQuota');
  366. $this->userManager->expects($this->never())
  367. ->method('get');
  368. $this->user->updateQuota();
  369. }
  370. /**
  371. * No quota attribute set and wrong default will set 'default' as quota
  372. */
  373. public function testUpdateWrongDefaultQuotaNotProvided(): void {
  374. $this->connection->expects($this->exactly(2))
  375. ->method('__get')
  376. ->willReturnMap([
  377. ['ldapQuotaAttribute', ''],
  378. ['ldapQuotaDefault', '23 GBwowowo']
  379. ]);
  380. $this->access->expects($this->never())
  381. ->method('readAttribute');
  382. $coreUser = $this->createMock(IUser::class);
  383. $coreUser->expects($this->never())
  384. ->method('setQuota');
  385. $this->userManager->expects($this->never())
  386. ->method('get');
  387. $this->user->updateQuota();
  388. }
  389. //the testUpdateAvatar series also implicitly tests getAvatarImage
  390. public function XtestUpdateAvatarJpegPhotoProvided() {
  391. $this->access->expects($this->once())
  392. ->method('readAttribute')
  393. ->with($this->equalTo($this->dn),
  394. $this->equalTo('jpegphoto'))
  395. ->willReturn(['this is a photo']);
  396. $this->image->expects($this->once())
  397. ->method('loadFromBase64')
  398. ->willReturn('imageResource');
  399. $this->image->expects($this->once())
  400. ->method('valid')
  401. ->willReturn(true);
  402. $this->image->expects($this->once())
  403. ->method('width')
  404. ->willReturn(128);
  405. $this->image->expects($this->once())
  406. ->method('height')
  407. ->willReturn(128);
  408. $this->image->expects($this->once())
  409. ->method('centerCrop')
  410. ->willReturn(true);
  411. $this->image->expects($this->once())
  412. ->method('data')
  413. ->willReturn('this is a photo');
  414. $this->config->expects($this->once())
  415. ->method('getUserValue')
  416. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', '')
  417. ->willReturn('');
  418. $this->config->expects($this->once())
  419. ->method('setUserValue')
  420. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', md5('this is a photo'));
  421. $this->filesystemhelper->expects($this->once())
  422. ->method('isLoaded')
  423. ->willReturn(true);
  424. $avatar = $this->createMock(IAvatar::class);
  425. $avatar->expects($this->once())
  426. ->method('set')
  427. ->with($this->isInstanceOf($this->image));
  428. $this->avatarManager->expects($this->once())
  429. ->method('getAvatar')
  430. ->with($this->equalTo($this->uid))
  431. ->willReturn($avatar);
  432. $this->connection->expects($this->any())
  433. ->method('resolveRule')
  434. ->with('avatar')
  435. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  436. $this->user->updateAvatar();
  437. }
  438. public function testUpdateAvatarKnownJpegPhotoProvided(): void {
  439. $this->access->expects($this->once())
  440. ->method('readAttribute')
  441. ->with($this->equalTo($this->dn),
  442. $this->equalTo('jpegphoto'))
  443. ->willReturn(['this is a photo']);
  444. $this->image->expects($this->once())
  445. ->method('loadFromBase64')
  446. ->willReturn('imageResource');
  447. $this->image->expects($this->never())
  448. ->method('valid');
  449. $this->image->expects($this->never())
  450. ->method('width');
  451. $this->image->expects($this->never())
  452. ->method('height');
  453. $this->image->expects($this->never())
  454. ->method('centerCrop');
  455. $this->image->expects($this->once())
  456. ->method('data')
  457. ->willReturn('this is a photo');
  458. $this->config->expects($this->once())
  459. ->method('getUserValue')
  460. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', '')
  461. ->willReturn(md5('this is a photo'));
  462. $this->config->expects($this->never())
  463. ->method('setUserValue');
  464. $this->filesystemhelper->expects($this->never())
  465. ->method('isLoaded');
  466. $avatar = $this->createMock(IAvatar::class);
  467. $avatar->expects($this->never())
  468. ->method('set');
  469. $avatar->expects($this->any())
  470. ->method('exists')
  471. ->willReturn(true);
  472. $avatar->expects($this->any())
  473. ->method('isCustomAvatar')
  474. ->willReturn(true);
  475. $this->avatarManager->expects($this->any())
  476. ->method('getAvatar')
  477. ->with($this->uid)
  478. ->willReturn($avatar);
  479. $this->connection->expects($this->any())
  480. ->method('resolveRule')
  481. ->with('avatar')
  482. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  483. $this->assertTrue($this->user->updateAvatar());
  484. }
  485. public function XtestUpdateAvatarThumbnailPhotoProvided() {
  486. $this->access->expects($this->any())
  487. ->method('readAttribute')
  488. ->willReturnCallback(function ($dn, $attr) {
  489. if ($dn === $this->dn
  490. && $attr === 'jpegphoto') {
  491. return false;
  492. } elseif ($dn === $this->dn
  493. && $attr === 'thumbnailphoto') {
  494. return ['this is a photo'];
  495. }
  496. return null;
  497. });
  498. $this->image->expects($this->once())
  499. ->method('loadFromBase64')
  500. ->willReturn('imageResource');
  501. $this->image->expects($this->once())
  502. ->method('valid')
  503. ->willReturn(true);
  504. $this->image->expects($this->once())
  505. ->method('width')
  506. ->willReturn(128);
  507. $this->image->expects($this->once())
  508. ->method('height')
  509. ->willReturn(128);
  510. $this->image->expects($this->once())
  511. ->method('centerCrop')
  512. ->willReturn(true);
  513. $this->image->expects($this->once())
  514. ->method('data')
  515. ->willReturn('this is a photo');
  516. $this->config->expects($this->once())
  517. ->method('getUserValue')
  518. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', '')
  519. ->willReturn('');
  520. $this->config->expects($this->once())
  521. ->method('setUserValue')
  522. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', md5('this is a photo'));
  523. $this->filesystemhelper->expects($this->once())
  524. ->method('isLoaded')
  525. ->willReturn(true);
  526. $avatar = $this->createMock(IAvatar::class);
  527. $avatar->expects($this->once())
  528. ->method('set')
  529. ->with($this->isInstanceOf($this->image));
  530. $this->avatarManager->expects($this->once())
  531. ->method('getAvatar')
  532. ->with($this->equalTo($this->uid))
  533. ->willReturn($avatar);
  534. $this->connection->expects($this->any())
  535. ->method('resolveRule')
  536. ->with('avatar')
  537. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  538. $this->user->updateAvatar();
  539. }
  540. public function testUpdateAvatarCorruptPhotoProvided(): void {
  541. $this->access->expects($this->any())
  542. ->method('readAttribute')
  543. ->willReturnCallback(function ($dn, $attr) {
  544. if ($dn === $this->dn
  545. && $attr === 'jpegphoto') {
  546. return false;
  547. } elseif ($dn === $this->dn
  548. && $attr === 'thumbnailphoto') {
  549. return ['this is a photo'];
  550. }
  551. return null;
  552. });
  553. $this->image->expects($this->once())
  554. ->method('loadFromBase64')
  555. ->willReturn(false);
  556. $this->image->expects($this->never())
  557. ->method('valid');
  558. $this->image->expects($this->never())
  559. ->method('width');
  560. $this->image->expects($this->never())
  561. ->method('height');
  562. $this->image->expects($this->never())
  563. ->method('centerCrop');
  564. $this->image->expects($this->never())
  565. ->method('data');
  566. $this->config->expects($this->never())
  567. ->method('getUserValue');
  568. $this->config->expects($this->never())
  569. ->method('setUserValue');
  570. $this->filesystemhelper->expects($this->never())
  571. ->method('isLoaded');
  572. $avatar = $this->createMock(IAvatar::class);
  573. $avatar->expects($this->never())
  574. ->method('set');
  575. $this->avatarManager->expects($this->never())
  576. ->method('getAvatar');
  577. $this->connection->expects($this->any())
  578. ->method('resolveRule')
  579. ->with('avatar')
  580. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  581. $this->user->updateAvatar();
  582. }
  583. public function XtestUpdateAvatarUnsupportedThumbnailPhotoProvided() {
  584. $this->access->expects($this->any())
  585. ->method('readAttribute')
  586. ->willReturnCallback(function ($dn, $attr) {
  587. if ($dn === $this->dn
  588. && $attr === 'jpegphoto') {
  589. return false;
  590. } elseif ($dn === $this->dn
  591. && $attr === 'thumbnailphoto') {
  592. return ['this is a photo'];
  593. }
  594. return null;
  595. });
  596. $this->image->expects($this->once())
  597. ->method('loadFromBase64')
  598. ->willReturn('imageResource');
  599. $this->image->expects($this->once())
  600. ->method('valid')
  601. ->willReturn(true);
  602. $this->image->expects($this->once())
  603. ->method('width')
  604. ->willReturn(128);
  605. $this->image->expects($this->once())
  606. ->method('height')
  607. ->willReturn(128);
  608. $this->image->expects($this->once())
  609. ->method('centerCrop')
  610. ->willReturn(true);
  611. $this->image->expects($this->once())
  612. ->method('data')
  613. ->willReturn('this is a photo');
  614. $this->config->expects($this->once())
  615. ->method('getUserValue')
  616. ->with($this->uid, 'user_ldap', 'lastAvatarChecksum', '')
  617. ->willReturn('');
  618. $this->config->expects($this->never())
  619. ->method('setUserValue');
  620. $this->filesystemhelper->expects($this->once())
  621. ->method('isLoaded')
  622. ->willReturn(true);
  623. $avatar = $this->createMock(IAvatar::class);
  624. $avatar->expects($this->once())
  625. ->method('set')
  626. ->with($this->isInstanceOf($this->image))
  627. ->willThrowException(new \Exception());
  628. $this->avatarManager->expects($this->once())
  629. ->method('getAvatar')
  630. ->with($this->equalTo($this->uid))
  631. ->willReturn($avatar);
  632. $this->connection->expects($this->any())
  633. ->method('resolveRule')
  634. ->with('avatar')
  635. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  636. $this->assertFalse($this->user->updateAvatar());
  637. }
  638. public function testUpdateAvatarNotProvided(): void {
  639. $this->access->expects($this->any())
  640. ->method('readAttribute')
  641. ->willReturnCallback(function ($dn, $attr) {
  642. if ($dn === $this->dn
  643. && $attr === 'jpegPhoto') {
  644. return false;
  645. } elseif ($dn === $this->dn
  646. && $attr === 'thumbnailPhoto') {
  647. return false;
  648. }
  649. return null;
  650. });
  651. $this->image->expects($this->never())
  652. ->method('valid');
  653. $this->image->expects($this->never())
  654. ->method('width');
  655. $this->image->expects($this->never())
  656. ->method('height');
  657. $this->image->expects($this->never())
  658. ->method('centerCrop');
  659. $this->image->expects($this->never())
  660. ->method('data');
  661. $this->config->expects($this->never())
  662. ->method('getUserValue');
  663. $this->config->expects($this->never())
  664. ->method('setUserValue');
  665. $this->filesystemhelper->expects($this->never())
  666. ->method('isLoaded');
  667. $this->avatarManager->expects($this->never())
  668. ->method('getAvatar');
  669. $this->connection->expects($this->any())
  670. ->method('resolveRule')
  671. ->with('avatar')
  672. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  673. $this->user->updateAvatar();
  674. }
  675. public function extStorageHomeDataProvider() {
  676. return [
  677. [ 'myFolder', null ],
  678. [ '', null, false ],
  679. [ 'myFolder', 'myFolder' ],
  680. ];
  681. }
  682. /**
  683. * @dataProvider extStorageHomeDataProvider
  684. */
  685. public function testUpdateExtStorageHome(string $expected, ?string $valueFromLDAP = null, bool $isSet = true): void {
  686. if ($valueFromLDAP === null) {
  687. $this->connection->expects($this->once())
  688. ->method('__get')
  689. ->willReturnMap([
  690. ['ldapExtStorageHomeAttribute', 'homeDirectory'],
  691. ]);
  692. $return = [];
  693. if ($isSet) {
  694. $return[] = $expected;
  695. }
  696. $this->access->expects($this->once())
  697. ->method('readAttribute')
  698. ->with($this->dn, 'homeDirectory')
  699. ->willReturn($return);
  700. }
  701. if ($expected !== '') {
  702. $this->config->expects($this->once())
  703. ->method('setUserValue')
  704. ->with($this->uid, 'user_ldap', 'extStorageHome', $expected);
  705. } else {
  706. $this->config->expects($this->once())
  707. ->method('deleteUserValue')
  708. ->with($this->uid, 'user_ldap', 'extStorageHome');
  709. }
  710. $actual = $this->user->updateExtStorageHome($valueFromLDAP);
  711. $this->assertSame($expected, $actual);
  712. }
  713. public function testMarkLogin(): void {
  714. $this->config->expects($this->once())
  715. ->method('setUserValue')
  716. ->with($this->equalTo($this->uid),
  717. $this->equalTo('user_ldap'),
  718. $this->equalTo(User::USER_PREFKEY_FIRSTLOGIN),
  719. $this->equalTo(1))
  720. ->willReturn(true);
  721. $this->user->markLogin();
  722. }
  723. public function testGetAvatarImageProvided(): void {
  724. $this->access->expects($this->once())
  725. ->method('readAttribute')
  726. ->with($this->equalTo($this->dn),
  727. $this->equalTo('jpegphoto'))
  728. ->willReturn(['this is a photo']);
  729. $this->connection->expects($this->any())
  730. ->method('resolveRule')
  731. ->with('avatar')
  732. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  733. $photo = $this->user->getAvatarImage();
  734. $this->assertSame('this is a photo', $photo);
  735. //make sure readAttribute is not called again but the already fetched
  736. //photo is returned
  737. $this->user->getAvatarImage();
  738. }
  739. public function testGetAvatarImageDisabled(): void {
  740. $this->access->expects($this->never())
  741. ->method('readAttribute')
  742. ->with($this->equalTo($this->dn), $this->anything());
  743. $this->connection->expects($this->any())
  744. ->method('resolveRule')
  745. ->with('avatar')
  746. ->willReturn([]);
  747. $this->assertFalse($this->user->getAvatarImage());
  748. }
  749. public function imageDataProvider() {
  750. return [
  751. [ false, false ],
  752. [ 'corruptData', false ],
  753. [ 'validData', true ],
  754. ];
  755. }
  756. public function testProcessAttributes(): void {
  757. $requiredMethods = [
  758. 'updateQuota',
  759. 'updateEmail',
  760. 'composeAndStoreDisplayName',
  761. 'storeLDAPUserName',
  762. 'getHomePath',
  763. 'updateAvatar',
  764. 'updateExtStorageHome',
  765. ];
  766. /** @var User|\PHPUnit\Framework\MockObject\MockObject $userMock */
  767. $userMock = $this->getMockBuilder(User::class)
  768. ->setConstructorArgs([
  769. $this->uid,
  770. $this->dn,
  771. $this->access,
  772. $this->config,
  773. $this->filesystemhelper,
  774. $this->image,
  775. $this->logger,
  776. $this->avatarManager,
  777. $this->userManager,
  778. $this->notificationManager
  779. ])
  780. ->setMethods($requiredMethods)
  781. ->getMock();
  782. $this->connection->setConfiguration([
  783. 'homeFolderNamingRule' => 'homeDirectory'
  784. ]);
  785. $this->connection->expects($this->any())
  786. ->method('__get')
  787. ->willReturnCallback(function ($name) {
  788. if ($name === 'homeFolderNamingRule') {
  789. return 'attr:homeDirectory';
  790. }
  791. return $name;
  792. });
  793. $this->connection->expects($this->any())
  794. ->method('resolveRule')
  795. ->with('avatar')
  796. ->willReturn(['jpegphoto', 'thumbnailphoto']);
  797. $record = [
  798. strtolower($this->connection->ldapQuotaAttribute) => ['4096'],
  799. strtolower($this->connection->ldapEmailAttribute) => ['alice@wonderland.org'],
  800. strtolower($this->connection->ldapUserDisplayName) => ['Aaaaalice'],
  801. strtolower($this->connection->ldapExtStorageHomeAttribute) => ['homeDirectory'],
  802. 'uid' => [$this->uid],
  803. 'homedirectory' => ['Alice\'s Folder'],
  804. 'memberof' => ['cn=groupOne', 'cn=groupTwo'],
  805. 'jpegphoto' => ['here be an image']
  806. ];
  807. foreach ($requiredMethods as $method) {
  808. $userMock->expects($this->once())
  809. ->method($method);
  810. }
  811. \OC_Hook::clear();//disconnect irrelevant hooks
  812. $userMock->processAttributes($record);
  813. /** @noinspection PhpUnhandledExceptionInspection */
  814. \OC_Hook::emit('OC_User', 'post_login', ['uid' => $this->uid]);
  815. }
  816. public function emptyHomeFolderAttributeValueProvider() {
  817. return [
  818. 'empty' => [''],
  819. 'prefixOnly' => ['attr:'],
  820. ];
  821. }
  822. /**
  823. * @dataProvider emptyHomeFolderAttributeValueProvider
  824. */
  825. public function testGetHomePathNotConfigured($attributeValue): void {
  826. $this->connection->expects($this->any())
  827. ->method('__get')
  828. ->with($this->equalTo('homeFolderNamingRule'))
  829. ->willReturn($attributeValue);
  830. $this->access->expects($this->never())
  831. ->method('readAttribute');
  832. $this->config->expects($this->never())
  833. ->method('getAppValue');
  834. /** @noinspection PhpUnhandledExceptionInspection */
  835. $this->assertFalse($this->user->getHomePath());
  836. }
  837. public function testGetHomePathConfiguredNotAvailableAllowed(): void {
  838. $this->connection->expects($this->any())
  839. ->method('__get')
  840. ->with($this->equalTo('homeFolderNamingRule'))
  841. ->willReturn('attr:foobar');
  842. $this->access->expects($this->once())
  843. ->method('readAttribute')
  844. ->willReturn(false);
  845. $this->access->expects($this->once())
  846. ->method('username2dn')
  847. ->willReturn($this->dn);
  848. // asks for "enforce_home_folder_naming_rule"
  849. $this->config->expects($this->once())
  850. ->method('getAppValue')
  851. ->willReturn(false);
  852. /** @noinspection PhpUnhandledExceptionInspection */
  853. $this->assertFalse($this->user->getHomePath());
  854. }
  855. public function testGetHomePathConfiguredNotAvailableNotAllowed(): void {
  856. $this->expectException(\Exception::class);
  857. $this->connection->expects($this->any())
  858. ->method('__get')
  859. ->with($this->equalTo('homeFolderNamingRule'))
  860. ->willReturn('attr:foobar');
  861. $this->access->expects($this->once())
  862. ->method('readAttribute')
  863. ->willReturn(false);
  864. $this->access->expects($this->once())
  865. ->method('username2dn')
  866. ->willReturn($this->dn);
  867. // asks for "enforce_home_folder_naming_rule"
  868. $this->config->expects($this->once())
  869. ->method('getAppValue')
  870. ->willReturn(true);
  871. $this->user->getHomePath();
  872. }
  873. public function displayNameProvider() {
  874. return [
  875. ['Roland Deschain', '', 'Roland Deschain', false],
  876. ['Roland Deschain', '', 'Roland Deschain', true],
  877. ['Roland Deschain', null, 'Roland Deschain', false],
  878. ['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)', false],
  879. ['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)', true],
  880. ];
  881. }
  882. /**
  883. * @dataProvider displayNameProvider
  884. */
  885. public function testComposeAndStoreDisplayName($part1, $part2, $expected, $expectTriggerChange): void {
  886. $this->config->expects($this->once())
  887. ->method('setUserValue');
  888. $oldName = $expectTriggerChange ? 'xxGunslingerxx' : null;
  889. $this->config->expects($this->once())
  890. ->method('getUserValue')
  891. ->with($this->user->getUsername(), 'user_ldap', 'displayName', null)
  892. ->willReturn($oldName);
  893. $ncUserObj = $this->createMock(\OC\User\User::class);
  894. if ($expectTriggerChange) {
  895. $ncUserObj->expects($this->once())
  896. ->method('triggerChange')
  897. ->with('displayName', $expected);
  898. } else {
  899. $ncUserObj->expects($this->never())
  900. ->method('triggerChange');
  901. }
  902. $this->userManager->expects($this->once())
  903. ->method('get')
  904. ->willReturn($ncUserObj);
  905. $displayName = $this->user->composeAndStoreDisplayName($part1, $part2);
  906. $this->assertSame($expected, $displayName);
  907. }
  908. public function testComposeAndStoreDisplayNameNoOverwrite(): void {
  909. $displayName = 'Randall Flagg';
  910. $this->config->expects($this->never())
  911. ->method('setUserValue');
  912. $this->config->expects($this->once())
  913. ->method('getUserValue')
  914. ->willReturn($displayName);
  915. $this->userManager->expects($this->never())
  916. ->method('get'); // Implicit: no triggerChange can be called
  917. $composedDisplayName = $this->user->composeAndStoreDisplayName($displayName);
  918. $this->assertSame($composedDisplayName, $displayName);
  919. }
  920. public function testHandlePasswordExpiryWarningDefaultPolicy(): void {
  921. $this->connection->expects($this->any())
  922. ->method('__get')
  923. ->willReturnCallback(function ($name) {
  924. if ($name === 'ldapDefaultPPolicyDN') {
  925. return 'cn=default,ou=policies,dc=foo,dc=bar';
  926. }
  927. if ($name === 'turnOnPasswordChange') {
  928. return '1';
  929. }
  930. return $name;
  931. });
  932. $this->access->expects($this->any())
  933. ->method('search')
  934. ->willReturnCallback(function ($filter, $base) {
  935. if ($base === $this->dn) {
  936. return [
  937. [
  938. 'pwdchangedtime' => [(new \DateTime())->sub(new \DateInterval('P28D'))->format('Ymdhis') . 'Z'],
  939. 'pwdgraceusetime' => [],
  940. ],
  941. ];
  942. }
  943. if ($base === 'cn=default,ou=policies,dc=foo,dc=bar') {
  944. return [
  945. [
  946. 'pwdmaxage' => ['2592000'],
  947. 'pwdexpirewarning' => ['2591999'],
  948. ],
  949. ];
  950. }
  951. return [];
  952. });
  953. $notification = $this->getMockBuilder(INotification::class)
  954. ->disableOriginalConstructor()
  955. ->getMock();
  956. $notification->expects($this->any())
  957. ->method('setApp')
  958. ->willReturn($notification);
  959. $notification->expects($this->any())
  960. ->method('setUser')
  961. ->willReturn($notification);
  962. $notification->expects($this->any())
  963. ->method('setObject')
  964. ->willReturn($notification);
  965. $notification->expects($this->any())
  966. ->method('setDateTime')
  967. ->willReturn($notification);
  968. $this->notificationManager->expects($this->exactly(2))
  969. ->method('createNotification')
  970. ->willReturn($notification);
  971. $this->notificationManager->expects($this->exactly(1))
  972. ->method('notify');
  973. \OC_Hook::clear();//disconnect irrelevant hooks
  974. \OCP\Util::connectHook('OC_User', 'post_login', $this->user, 'handlePasswordExpiry');
  975. /** @noinspection PhpUnhandledExceptionInspection */
  976. \OC_Hook::emit('OC_User', 'post_login', ['uid' => $this->uid]);
  977. }
  978. public function testHandlePasswordExpiryWarningCustomPolicy(): void {
  979. $this->connection->expects($this->any())
  980. ->method('__get')
  981. ->willReturnCallback(function ($name) {
  982. if ($name === 'ldapDefaultPPolicyDN') {
  983. return 'cn=default,ou=policies,dc=foo,dc=bar';
  984. }
  985. if ($name === 'turnOnPasswordChange') {
  986. return '1';
  987. }
  988. return $name;
  989. });
  990. $this->access->expects($this->any())
  991. ->method('search')
  992. ->willReturnCallback(function ($filter, $base) {
  993. if ($base === $this->dn) {
  994. return [
  995. [
  996. 'pwdpolicysubentry' => ['cn=custom,ou=policies,dc=foo,dc=bar'],
  997. 'pwdchangedtime' => [(new \DateTime())->sub(new \DateInterval('P28D'))->format('Ymdhis') . 'Z'],
  998. 'pwdgraceusetime' => [],
  999. ]
  1000. ];
  1001. }
  1002. if ($base === 'cn=custom,ou=policies,dc=foo,dc=bar') {
  1003. return [
  1004. [
  1005. 'pwdmaxage' => ['2592000'],
  1006. 'pwdexpirewarning' => ['2591999'],
  1007. ]
  1008. ];
  1009. }
  1010. return [];
  1011. });
  1012. $notification = $this->getMockBuilder(INotification::class)
  1013. ->disableOriginalConstructor()
  1014. ->getMock();
  1015. $notification->expects($this->any())
  1016. ->method('setApp')
  1017. ->willReturn($notification);
  1018. $notification->expects($this->any())
  1019. ->method('setUser')
  1020. ->willReturn($notification);
  1021. $notification->expects($this->any())
  1022. ->method('setObject')
  1023. ->willReturn($notification);
  1024. $notification->expects($this->any())
  1025. ->method('setDateTime')
  1026. ->willReturn($notification);
  1027. $this->notificationManager->expects($this->exactly(2))
  1028. ->method('createNotification')
  1029. ->willReturn($notification);
  1030. $this->notificationManager->expects($this->exactly(1))
  1031. ->method('notify');
  1032. \OC_Hook::clear();//disconnect irrelevant hooks
  1033. \OCP\Util::connectHook('OC_User', 'post_login', $this->user, 'handlePasswordExpiry');
  1034. /** @noinspection PhpUnhandledExceptionInspection */
  1035. \OC_Hook::emit('OC_User', 'post_login', ['uid' => $this->uid]);
  1036. }
  1037. }