LostControllerTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <?php
  2. /**
  3. * @author Lukas Reschke <lukas@owncloud.com>
  4. *
  5. * @copyright Copyright (c) 2015, ownCloud, Inc.
  6. * @license AGPL-3.0
  7. *
  8. * This code is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License, version 3,
  10. * as published by the Free Software Foundation.
  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 Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License, version 3,
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>
  19. *
  20. */
  21. namespace Tests\Core\Controller;
  22. use OC\Core\Controller\LostController;
  23. use OCP\AppFramework\Http\TemplateResponse;
  24. use OCP\AppFramework\Utility\ITimeFactory;
  25. use OCP\IConfig;
  26. use OCP\IL10N;
  27. use OCP\IRequest;
  28. use OCP\IURLGenerator;
  29. use OCP\IUser;
  30. use OCP\IUserManager;
  31. use OCP\Mail\IMailer;
  32. use OCP\Security\ISecureRandom;
  33. use PHPUnit_Framework_MockObject_MockObject;
  34. /**
  35. * Class LostControllerTest
  36. *
  37. * @package OC\Core\Controller
  38. */
  39. class LostControllerTest extends \PHPUnit_Framework_TestCase {
  40. /** @var LostController */
  41. private $lostController;
  42. /** @var IUser */
  43. private $existingUser;
  44. /** @var IURLGenerator | PHPUnit_Framework_MockObject_MockObject */
  45. private $urlGenerator;
  46. /** @var IL10N */
  47. private $l10n;
  48. /** @var IUserManager | PHPUnit_Framework_MockObject_MockObject */
  49. private $userManager;
  50. /** @var \OC_Defaults */
  51. private $defaults;
  52. /** @var IConfig | PHPUnit_Framework_MockObject_MockObject */
  53. private $config;
  54. /** @var IMailer | PHPUnit_Framework_MockObject_MockObject */
  55. private $mailer;
  56. /** @var ISecureRandom | PHPUnit_Framework_MockObject_MockObject */
  57. private $secureRandom;
  58. /** @var ITimeFactory | PHPUnit_Framework_MockObject_MockObject */
  59. private $timeFactory;
  60. /** @var IRequest */
  61. private $request;
  62. protected function setUp() {
  63. $this->existingUser = $this->getMockBuilder('OCP\IUser')
  64. ->disableOriginalConstructor()->getMock();
  65. $this->existingUser
  66. ->expects($this->any())
  67. ->method('getEMailAddress')
  68. ->willReturn('test@example.com');
  69. $this->config = $this->getMockBuilder('\OCP\IConfig')
  70. ->disableOriginalConstructor()->getMock();
  71. $this->l10n = $this->getMockBuilder('\OCP\IL10N')
  72. ->disableOriginalConstructor()->getMock();
  73. $this->l10n
  74. ->expects($this->any())
  75. ->method('t')
  76. ->will($this->returnCallback(function($text, $parameters = array()) {
  77. return vsprintf($text, $parameters);
  78. }));
  79. $this->defaults = $this->getMockBuilder('\OC_Defaults')
  80. ->disableOriginalConstructor()->getMock();
  81. $this->userManager = $this->getMockBuilder('\OCP\IUserManager')
  82. ->disableOriginalConstructor()->getMock();
  83. $this->urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator')
  84. ->disableOriginalConstructor()->getMock();
  85. $this->mailer = $this->getMockBuilder('\OCP\Mail\IMailer')
  86. ->disableOriginalConstructor()->getMock();
  87. $this->secureRandom = $this->getMockBuilder('\OCP\Security\ISecureRandom')
  88. ->disableOriginalConstructor()->getMock();
  89. $this->timeFactory = $this->getMockBuilder('\OCP\AppFramework\Utility\ITimeFactory')
  90. ->disableOriginalConstructor()->getMock();
  91. $this->request = $this->getMockBuilder('OCP\IRequest')
  92. ->disableOriginalConstructor()->getMock();
  93. $this->lostController = new LostController(
  94. 'Core',
  95. $this->request,
  96. $this->urlGenerator,
  97. $this->userManager,
  98. $this->defaults,
  99. $this->l10n,
  100. $this->config,
  101. $this->secureRandom,
  102. 'lostpassword-noreply@localhost',
  103. true,
  104. $this->mailer,
  105. $this->timeFactory
  106. );
  107. }
  108. public function testResetFormInvalidToken() {
  109. $userId = 'admin';
  110. $token = 'MySecretToken';
  111. $response = $this->lostController->resetform($token, $userId);
  112. $expectedResponse = new TemplateResponse('core',
  113. 'error',
  114. [
  115. 'errors' => [
  116. ['error' => 'Couldn\'t reset password because the token is invalid'],
  117. ]
  118. ],
  119. 'guest');
  120. $this->assertEquals($expectedResponse, $response);
  121. }
  122. public function testResetFormInvalidTokenMatch() {
  123. $this->config
  124. ->expects($this->once())
  125. ->method('getUserValue')
  126. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  127. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  128. $user = $this->getMockBuilder('\OCP\IUser')
  129. ->disableOriginalConstructor()->getMock();
  130. $user
  131. ->expects($this->once())
  132. ->method('getLastLogin')
  133. ->will($this->returnValue(12344));
  134. $this->userManager
  135. ->expects($this->once())
  136. ->method('get')
  137. ->with('ValidTokenUser')
  138. ->will($this->returnValue($user));
  139. $userId = 'ValidTokenUser';
  140. $token = '12345:MySecretToken';
  141. $response = $this->lostController->resetform($token, $userId);
  142. $expectedResponse = new TemplateResponse('core',
  143. 'error',
  144. [
  145. 'errors' => [
  146. ['error' => 'Couldn\'t reset password because the token is invalid'],
  147. ]
  148. ],
  149. 'guest');
  150. $this->assertEquals($expectedResponse, $response);
  151. }
  152. public function testResetFormExpiredToken() {
  153. $userId = 'ValidTokenUser';
  154. $token = '12345:TheOnlyAndOnlyOneTokenToResetThePassword';
  155. $user = $this->getMockBuilder('\OCP\IUser')
  156. ->disableOriginalConstructor()->getMock();
  157. $this->userManager
  158. ->expects($this->once())
  159. ->method('get')
  160. ->with('ValidTokenUser')
  161. ->will($this->returnValue($user));
  162. $this->timeFactory
  163. ->expects($this->once())
  164. ->method('getTime')
  165. ->will($this->returnValue(12345*60*60*12));
  166. $userId = 'ValidTokenUser';
  167. $token = 'TheOnlyAndOnlyOneTokenToResetThePassword';
  168. $this->config
  169. ->expects($this->once())
  170. ->method('getUserValue')
  171. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  172. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  173. $response = $this->lostController->resetform($token, $userId);
  174. $expectedResponse = new TemplateResponse('core',
  175. 'error',
  176. [
  177. 'errors' => [
  178. ['error' => 'Couldn\'t reset password because the token is expired'],
  179. ]
  180. ],
  181. 'guest');
  182. $this->assertEquals($expectedResponse, $response);
  183. }
  184. public function testResetFormValidToken() {
  185. $userId = 'ValidTokenUser';
  186. $token = '12345:TheOnlyAndOnlyOneTokenToResetThePassword';
  187. $user = $this->getMockBuilder('\OCP\IUser')
  188. ->disableOriginalConstructor()->getMock();
  189. $user
  190. ->expects($this->once())
  191. ->method('getLastLogin')
  192. ->will($this->returnValue(12344));
  193. $this->userManager
  194. ->expects($this->once())
  195. ->method('get')
  196. ->with('ValidTokenUser')
  197. ->will($this->returnValue($user));
  198. $this->timeFactory
  199. ->expects($this->once())
  200. ->method('getTime')
  201. ->will($this->returnValue(12348));
  202. $userId = 'ValidTokenUser';
  203. $token = 'TheOnlyAndOnlyOneTokenToResetThePassword';
  204. $this->config
  205. ->expects($this->once())
  206. ->method('getUserValue')
  207. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  208. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  209. $this->urlGenerator
  210. ->expects($this->once())
  211. ->method('linkToRouteAbsolute')
  212. ->with('core.lost.setPassword', array('userId' => 'ValidTokenUser', 'token' => 'TheOnlyAndOnlyOneTokenToResetThePassword'))
  213. ->will($this->returnValue('https://ownCloud.com/index.php/lostpassword/'));
  214. $response = $this->lostController->resetform($token, $userId);
  215. $expectedResponse = new TemplateResponse('core',
  216. 'lostpassword/resetpassword',
  217. array(
  218. 'link' => 'https://ownCloud.com/index.php/lostpassword/',
  219. ),
  220. 'guest');
  221. $this->assertEquals($expectedResponse, $response);
  222. }
  223. public function testEmailUnsucessful() {
  224. $existingUser = 'ExistingUser';
  225. $nonExistingUser = 'NonExistingUser';
  226. $this->userManager
  227. ->expects($this->any())
  228. ->method('userExists')
  229. ->will($this->returnValueMap(array(
  230. array(true, $existingUser),
  231. array(false, $nonExistingUser)
  232. )));
  233. // With a non existing user
  234. $response = $this->lostController->email($nonExistingUser);
  235. $expectedResponse = [
  236. 'status' => 'error',
  237. 'msg' => 'Couldn\'t send reset email. Please make sure your username is correct.'
  238. ];
  239. $this->assertSame($expectedResponse, $response);
  240. // With no mail address
  241. $this->config
  242. ->expects($this->any())
  243. ->method('getUserValue')
  244. ->with($existingUser, 'settings', 'email')
  245. ->will($this->returnValue(null));
  246. $response = $this->lostController->email($existingUser);
  247. $expectedResponse = [
  248. 'status' => 'error',
  249. 'msg' => 'Couldn\'t send reset email. Please make sure your username is correct.'
  250. ];
  251. $this->assertSame($expectedResponse, $response);
  252. }
  253. public function testEmailSuccessful() {
  254. $this->secureRandom
  255. ->expects($this->once())
  256. ->method('generate')
  257. ->with('21')
  258. ->will($this->returnValue('ThisIsMaybeANotSoSecretToken!'));
  259. $this->userManager
  260. ->expects($this->once())
  261. ->method('userExists')
  262. ->with('ExistingUser')
  263. ->will($this->returnValue(true));
  264. $this->userManager
  265. ->expects($this->any())
  266. ->method('get')
  267. ->with('ExistingUser')
  268. ->willReturn($this->existingUser);
  269. $this->timeFactory
  270. ->expects($this->once())
  271. ->method('getTime')
  272. ->will($this->returnValue(12348));
  273. $this->config
  274. ->expects($this->once())
  275. ->method('setUserValue')
  276. ->with('ExistingUser', 'owncloud', 'lostpassword', '12348:ThisIsMaybeANotSoSecretToken!');
  277. $this->urlGenerator
  278. ->expects($this->once())
  279. ->method('linkToRouteAbsolute')
  280. ->with('core.lost.resetform', array('userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'))
  281. ->will($this->returnValue('https://ownCloud.com/index.php/lostpassword/'));
  282. $message = $this->getMockBuilder('\OC\Mail\Message')
  283. ->disableOriginalConstructor()->getMock();
  284. $message
  285. ->expects($this->at(0))
  286. ->method('setTo')
  287. ->with(['test@example.com' => 'ExistingUser']);
  288. $message
  289. ->expects($this->at(1))
  290. ->method('setSubject')
  291. ->with(' password reset');
  292. $message
  293. ->expects($this->at(2))
  294. ->method('setPlainBody')
  295. ->with('Use the following link to reset your password: https://ownCloud.com/index.php/lostpassword/');
  296. $message
  297. ->expects($this->at(3))
  298. ->method('setFrom')
  299. ->with(['lostpassword-noreply@localhost' => null]);
  300. $this->mailer
  301. ->expects($this->at(0))
  302. ->method('createMessage')
  303. ->will($this->returnValue($message));
  304. $this->mailer
  305. ->expects($this->at(1))
  306. ->method('send')
  307. ->with($message);
  308. $response = $this->lostController->email('ExistingUser');
  309. $expectedResponse = array('status' => 'success');
  310. $this->assertSame($expectedResponse, $response);
  311. }
  312. public function testEmailCantSendException() {
  313. $this->secureRandom
  314. ->expects($this->once())
  315. ->method('generate')
  316. ->with('21')
  317. ->will($this->returnValue('ThisIsMaybeANotSoSecretToken!'));
  318. $this->userManager
  319. ->expects($this->once())
  320. ->method('userExists')
  321. ->with('ExistingUser')
  322. ->will($this->returnValue(true));
  323. $this->userManager
  324. ->expects($this->any())
  325. ->method('get')
  326. ->with('ExistingUser')
  327. ->willReturn($this->existingUser);
  328. $this->config
  329. ->expects($this->once())
  330. ->method('setUserValue')
  331. ->with('ExistingUser', 'owncloud', 'lostpassword', '12348:ThisIsMaybeANotSoSecretToken!');
  332. $this->timeFactory
  333. ->expects($this->once())
  334. ->method('getTime')
  335. ->will($this->returnValue(12348));
  336. $this->urlGenerator
  337. ->expects($this->once())
  338. ->method('linkToRouteAbsolute')
  339. ->with('core.lost.resetform', array('userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'))
  340. ->will($this->returnValue('https://ownCloud.com/index.php/lostpassword/'));
  341. $message = $this->getMockBuilder('\OC\Mail\Message')
  342. ->disableOriginalConstructor()->getMock();
  343. $message
  344. ->expects($this->at(0))
  345. ->method('setTo')
  346. ->with(['test@example.com' => 'ExistingUser']);
  347. $message
  348. ->expects($this->at(1))
  349. ->method('setSubject')
  350. ->with(' password reset');
  351. $message
  352. ->expects($this->at(2))
  353. ->method('setPlainBody')
  354. ->with('Use the following link to reset your password: https://ownCloud.com/index.php/lostpassword/');
  355. $message
  356. ->expects($this->at(3))
  357. ->method('setFrom')
  358. ->with(['lostpassword-noreply@localhost' => null]);
  359. $this->mailer
  360. ->expects($this->at(0))
  361. ->method('createMessage')
  362. ->will($this->returnValue($message));
  363. $this->mailer
  364. ->expects($this->at(1))
  365. ->method('send')
  366. ->with($message)
  367. ->will($this->throwException(new \Exception()));
  368. $response = $this->lostController->email('ExistingUser');
  369. $expectedResponse = ['status' => 'error', 'msg' => 'Couldn\'t send reset email. Please contact your administrator.'];
  370. $this->assertSame($expectedResponse, $response);
  371. }
  372. public function testSetPasswordUnsuccessful() {
  373. $this->config
  374. ->expects($this->once())
  375. ->method('getUserValue')
  376. ->with('InvalidTokenUser', 'owncloud', 'lostpassword', null)
  377. ->will($this->returnValue('TheOnlyAndOnlyOneTokenToResetThePassword'));
  378. // With an invalid token
  379. $userName = 'InvalidTokenUser';
  380. $response = $this->lostController->setPassword('wrongToken', $userName, 'NewPassword', true);
  381. $expectedResponse = [
  382. 'status' => 'error',
  383. 'msg' => 'Couldn\'t reset password because the token is invalid'
  384. ];
  385. $this->assertSame($expectedResponse, $response);
  386. // With a valid token and no proceed
  387. $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword!', $userName, 'NewPassword', false);
  388. $expectedResponse = ['status' => 'error', 'msg' => '', 'encryption' => true];
  389. $this->assertSame($expectedResponse, $response);
  390. }
  391. public function testSetPasswordSuccessful() {
  392. $this->config
  393. ->expects($this->once())
  394. ->method('getUserValue')
  395. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  396. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  397. $user = $this->getMockBuilder('\OCP\IUser')
  398. ->disableOriginalConstructor()->getMock();
  399. $user
  400. ->expects($this->once())
  401. ->method('getLastLogin')
  402. ->will($this->returnValue(12344));
  403. $user->expects($this->once())
  404. ->method('setPassword')
  405. ->with('NewPassword')
  406. ->will($this->returnValue(true));
  407. $this->userManager
  408. ->expects($this->exactly(2))
  409. ->method('get')
  410. ->with('ValidTokenUser')
  411. ->will($this->returnValue($user));
  412. $this->config
  413. ->expects($this->once())
  414. ->method('deleteUserValue')
  415. ->with('ValidTokenUser', 'owncloud', 'lostpassword');
  416. $this->timeFactory
  417. ->expects($this->once())
  418. ->method('getTime')
  419. ->will($this->returnValue(12348));
  420. $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
  421. $expectedResponse = array('status' => 'success');
  422. $this->assertSame($expectedResponse, $response);
  423. }
  424. public function testSetPasswordExpiredToken() {
  425. $this->config
  426. ->expects($this->once())
  427. ->method('getUserValue')
  428. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  429. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  430. $user = $this->getMockBuilder('\OCP\IUser')
  431. ->disableOriginalConstructor()->getMock();
  432. $this->userManager
  433. ->expects($this->once())
  434. ->method('get')
  435. ->with('ValidTokenUser')
  436. ->will($this->returnValue($user));
  437. $this->timeFactory
  438. ->expects($this->once())
  439. ->method('getTime')
  440. ->will($this->returnValue(55546));
  441. $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
  442. $expectedResponse = [
  443. 'status' => 'error',
  444. 'msg' => 'Couldn\'t reset password because the token is expired',
  445. ];
  446. $this->assertSame($expectedResponse, $response);
  447. }
  448. public function testSetPasswordInvalidDataInDb() {
  449. $this->config
  450. ->expects($this->once())
  451. ->method('getUserValue')
  452. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  453. ->will($this->returnValue('TheOnlyAndOnlyOneTokenToResetThePassword'));
  454. $user = $this->getMockBuilder('\OCP\IUser')
  455. ->disableOriginalConstructor()->getMock();
  456. $this->userManager
  457. ->expects($this->once())
  458. ->method('get')
  459. ->with('ValidTokenUser')
  460. ->will($this->returnValue($user));
  461. $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
  462. $expectedResponse = [
  463. 'status' => 'error',
  464. 'msg' => 'Couldn\'t reset password because the token is invalid',
  465. ];
  466. $this->assertSame($expectedResponse, $response);
  467. }
  468. public function testSetPasswordExpiredTokenDueToLogin() {
  469. $this->config
  470. ->expects($this->once())
  471. ->method('getUserValue')
  472. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  473. ->will($this->returnValue('12345:TheOnlyAndOnlyOneTokenToResetThePassword'));
  474. $user = $this->getMockBuilder('\OCP\IUser')
  475. ->disableOriginalConstructor()->getMock();
  476. $user
  477. ->expects($this->once())
  478. ->method('getLastLogin')
  479. ->will($this->returnValue(12346));
  480. $this->userManager
  481. ->expects($this->once())
  482. ->method('get')
  483. ->with('ValidTokenUser')
  484. ->will($this->returnValue($user));
  485. $this->timeFactory
  486. ->expects($this->once())
  487. ->method('getTime')
  488. ->will($this->returnValue(12345));
  489. $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
  490. $expectedResponse = [
  491. 'status' => 'error',
  492. 'msg' => 'Couldn\'t reset password because the token is expired',
  493. ];
  494. $this->assertSame($expectedResponse, $response);
  495. }
  496. public function testIsSetPasswordWithoutTokenFailing() {
  497. $this->config
  498. ->expects($this->once())
  499. ->method('getUserValue')
  500. ->with('ValidTokenUser', 'owncloud', 'lostpassword', null)
  501. ->will($this->returnValue(null));
  502. $response = $this->lostController->setPassword('', 'ValidTokenUser', 'NewPassword', true);
  503. $expectedResponse = [
  504. 'status' => 'error',
  505. 'msg' => 'Couldn\'t reset password because the token is invalid'
  506. ];
  507. $this->assertSame($expectedResponse, $response);
  508. }
  509. }