LoginControllerTest.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. <?php
  2. /**
  3. * @author Lukas Reschke <lukas@owncloud.com>
  4. *
  5. * @copyright Copyright (c) 2016, 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\Authentication\Token\IToken;
  23. use OC\Authentication\TwoFactorAuth\Manager;
  24. use OC\Authentication\TwoFactorAuth\ProviderSet;
  25. use OC\Core\Controller\LoginController;
  26. use OC\Security\Bruteforce\Throttler;
  27. use OC\User\Session;
  28. use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
  29. use OCP\AppFramework\Http\RedirectResponse;
  30. use OCP\AppFramework\Http\TemplateResponse;
  31. use OCP\Authentication\TwoFactorAuth\IProvider;
  32. use OCP\Defaults;
  33. use OCP\IConfig;
  34. use OCP\ILogger;
  35. use OCP\IRequest;
  36. use OCP\ISession;
  37. use OCP\IURLGenerator;
  38. use OCP\IUser;
  39. use OCP\IUserManager;
  40. use Test\TestCase;
  41. class LoginControllerTest extends TestCase {
  42. /** @var LoginController */
  43. private $loginController;
  44. /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
  45. private $request;
  46. /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
  47. private $userManager;
  48. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
  49. private $config;
  50. /** @var ISession|\PHPUnit_Framework_MockObject_MockObject */
  51. private $session;
  52. /** @var Session|\PHPUnit_Framework_MockObject_MockObject */
  53. private $userSession;
  54. /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
  55. private $urlGenerator;
  56. /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
  57. private $logger;
  58. /** @var Manager|\PHPUnit_Framework_MockObject_MockObject */
  59. private $twoFactorManager;
  60. /** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */
  61. private $defaults;
  62. /** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */
  63. private $throttler;
  64. public function setUp() {
  65. parent::setUp();
  66. $this->request = $this->createMock(IRequest::class);
  67. $this->userManager = $this->createMock(\OC\User\Manager::class);
  68. $this->config = $this->createMock(IConfig::class);
  69. $this->session = $this->createMock(ISession::class);
  70. $this->userSession = $this->createMock(Session::class);
  71. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  72. $this->logger = $this->createMock(ILogger::class);
  73. $this->twoFactorManager = $this->createMock(Manager::class);
  74. $this->defaults = $this->createMock(Defaults::class);
  75. $this->throttler = $this->createMock(Throttler::class);
  76. $this->request->method('getRemoteAddress')
  77. ->willReturn('1.2.3.4');
  78. $this->throttler->method('getDelay')
  79. ->with(
  80. $this->equalTo('1.2.3.4'),
  81. $this->equalTo('')
  82. )->willReturn(1000);
  83. $this->loginController = new LoginController(
  84. 'core',
  85. $this->request,
  86. $this->userManager,
  87. $this->config,
  88. $this->session,
  89. $this->userSession,
  90. $this->urlGenerator,
  91. $this->logger,
  92. $this->twoFactorManager,
  93. $this->defaults,
  94. $this->throttler
  95. );
  96. }
  97. public function testLogoutWithoutToken() {
  98. $this->request
  99. ->expects($this->once())
  100. ->method('getCookie')
  101. ->with('nc_token')
  102. ->willReturn(null);
  103. $this->config
  104. ->expects($this->never())
  105. ->method('deleteUserValue');
  106. $this->urlGenerator
  107. ->expects($this->once())
  108. ->method('linkToRouteAbsolute')
  109. ->with('core.login.showLoginForm')
  110. ->willReturn('/login');
  111. $expected = new RedirectResponse('/login');
  112. $expected->addHeader('Clear-Site-Data', '"cache", "storage"');
  113. $this->assertEquals($expected, $this->loginController->logout());
  114. }
  115. public function testLogoutWithToken() {
  116. $this->request
  117. ->expects($this->once())
  118. ->method('getCookie')
  119. ->with('nc_token')
  120. ->willReturn('MyLoginToken');
  121. $user = $this->createMock(IUser::class);
  122. $user
  123. ->expects($this->once())
  124. ->method('getUID')
  125. ->willReturn('JohnDoe');
  126. $this->userSession
  127. ->expects($this->once())
  128. ->method('getUser')
  129. ->willReturn($user);
  130. $this->config
  131. ->expects($this->once())
  132. ->method('deleteUserValue')
  133. ->with('JohnDoe', 'login_token', 'MyLoginToken');
  134. $this->urlGenerator
  135. ->expects($this->once())
  136. ->method('linkToRouteAbsolute')
  137. ->with('core.login.showLoginForm')
  138. ->willReturn('/login');
  139. $expected = new RedirectResponse('/login');
  140. $expected->addHeader('Clear-Site-Data', '"cache", "storage"');
  141. $this->assertEquals($expected, $this->loginController->logout());
  142. }
  143. public function testShowLoginFormForLoggedInUsers() {
  144. $this->userSession
  145. ->expects($this->once())
  146. ->method('isLoggedIn')
  147. ->willReturn(true);
  148. $expectedResponse = new RedirectResponse(\OC_Util::getDefaultPageUrl());
  149. $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', '', ''));
  150. }
  151. public function testShowLoginFormWithErrorsInSession() {
  152. $this->userSession
  153. ->expects($this->once())
  154. ->method('isLoggedIn')
  155. ->willReturn(false);
  156. $this->session
  157. ->expects($this->once())
  158. ->method('get')
  159. ->with('loginMessages')
  160. ->willReturn(
  161. [
  162. [
  163. 'ErrorArray1',
  164. 'ErrorArray2',
  165. ],
  166. [
  167. 'MessageArray1',
  168. 'MessageArray2',
  169. ],
  170. ]
  171. );
  172. $expectedResponse = new TemplateResponse(
  173. 'core',
  174. 'login',
  175. [
  176. 'ErrorArray1' => true,
  177. 'ErrorArray2' => true,
  178. 'messages' => [
  179. 'MessageArray1',
  180. 'MessageArray2',
  181. ],
  182. 'loginName' => '',
  183. 'user_autofocus' => true,
  184. 'canResetPassword' => true,
  185. 'alt_login' => [],
  186. 'resetPasswordLink' => null,
  187. 'throttle_delay' => 1000,
  188. 'login_form_autocomplete' => 'off',
  189. ],
  190. 'guest'
  191. );
  192. $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', '', ''));
  193. }
  194. public function testShowLoginFormForFlowAuth() {
  195. $this->userSession
  196. ->expects($this->once())
  197. ->method('isLoggedIn')
  198. ->willReturn(false);
  199. $expectedResponse = new TemplateResponse(
  200. 'core',
  201. 'login',
  202. [
  203. 'messages' => [],
  204. 'redirect_url' => 'login/flow',
  205. 'loginName' => '',
  206. 'user_autofocus' => true,
  207. 'canResetPassword' => true,
  208. 'alt_login' => [],
  209. 'resetPasswordLink' => null,
  210. 'throttle_delay' => 1000,
  211. 'login_form_autocomplete' => 'off',
  212. ],
  213. 'guest'
  214. );
  215. $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', 'login/flow', ''));
  216. }
  217. /**
  218. * @return array
  219. */
  220. public function passwordResetDataProvider() {
  221. return [
  222. [
  223. true,
  224. true,
  225. ],
  226. [
  227. false,
  228. false,
  229. ],
  230. ];
  231. }
  232. /**
  233. * @dataProvider passwordResetDataProvider
  234. */
  235. public function testShowLoginFormWithPasswordResetOption($canChangePassword,
  236. $expectedResult) {
  237. $this->userSession
  238. ->expects($this->once())
  239. ->method('isLoggedIn')
  240. ->willReturn(false);
  241. $this->config
  242. ->expects($this->exactly(2))
  243. ->method('getSystemValue')
  244. ->will($this->returnValueMap([
  245. ['login_form_autocomplete', true, true],
  246. ['lost_password_link', '', false],
  247. ]));
  248. $user = $this->createMock(IUser::class);
  249. $user
  250. ->expects($this->once())
  251. ->method('canChangePassword')
  252. ->willReturn($canChangePassword);
  253. $this->userManager
  254. ->expects($this->once())
  255. ->method('get')
  256. ->with('LdapUser')
  257. ->willReturn($user);
  258. $expectedResponse = new TemplateResponse(
  259. 'core',
  260. 'login',
  261. [
  262. 'messages' => [],
  263. 'loginName' => 'LdapUser',
  264. 'user_autofocus' => false,
  265. 'canResetPassword' => $expectedResult,
  266. 'alt_login' => [],
  267. 'resetPasswordLink' => false,
  268. 'throttle_delay' => 1000,
  269. 'login_form_autocomplete' => 'on',
  270. ],
  271. 'guest'
  272. );
  273. $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('LdapUser', '', ''));
  274. }
  275. /**
  276. * Asserts that a disabled user can't login and gets the expected response.
  277. */
  278. public function testLoginForDisabledUser() {
  279. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  280. $user = $this->createMock(IUser::class);
  281. $user->method('getUID')
  282. ->willReturn('uid');
  283. $user->method('isEnabled')
  284. ->willReturn(false);
  285. $this->request
  286. ->expects($this->once())
  287. ->method('passesCSRFCheck')
  288. ->willReturn(true);
  289. $this->userSession
  290. ->method('isLoggedIn')
  291. ->willReturn(false);
  292. $loginName = 'iMDisabled';
  293. $password = 'secret';
  294. $this->session
  295. ->expects($this->once())
  296. ->method('set')
  297. ->with('loginMessages', [
  298. [LoginController::LOGIN_MSG_USERDISABLED], []
  299. ]);
  300. $this->userManager
  301. ->expects($this->once())
  302. ->method('get')
  303. ->with($loginName)
  304. ->willReturn($user);
  305. $expected = new RedirectResponse('');
  306. $expected->throttle(['user' => $loginName]);
  307. $response = $this->loginController->tryLogin(
  308. $loginName, $password, null, false, 'Europe/Berlin', '1'
  309. );
  310. $this->assertEquals($expected, $response);
  311. }
  312. public function testShowLoginFormForUserNamed0() {
  313. $this->userSession
  314. ->expects($this->once())
  315. ->method('isLoggedIn')
  316. ->willReturn(false);
  317. $this->config
  318. ->expects($this->exactly(2))
  319. ->method('getSystemValue')
  320. ->will($this->returnValueMap([
  321. ['login_form_autocomplete', true, true],
  322. ['lost_password_link', '', false],
  323. ]));
  324. $user = $this->createMock(IUser::class);
  325. $user->expects($this->once())
  326. ->method('canChangePassword')
  327. ->willReturn(false);
  328. $this->userManager
  329. ->expects($this->once())
  330. ->method('get')
  331. ->with('0')
  332. ->willReturn($user);
  333. $expectedResponse = new TemplateResponse(
  334. 'core',
  335. 'login',
  336. [
  337. 'messages' => [],
  338. 'loginName' => '0',
  339. 'user_autofocus' => false,
  340. 'canResetPassword' => false,
  341. 'alt_login' => [],
  342. 'resetPasswordLink' => false,
  343. 'throttle_delay' => 1000,
  344. 'login_form_autocomplete' => 'on',
  345. ],
  346. 'guest'
  347. );
  348. $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('0', '', ''));
  349. }
  350. public function testLoginWithInvalidCredentials() {
  351. $user = 'MyUserName';
  352. $password = 'secret';
  353. $loginPageUrl = '/login?redirect_url=/apps/files';
  354. $this->request
  355. ->expects($this->once())
  356. ->method('passesCSRFCheck')
  357. ->willReturn(true);
  358. $this->userManager->expects($this->once())
  359. ->method('checkPasswordNoLogging')
  360. ->will($this->returnValue(false));
  361. $this->userManager->expects($this->once())
  362. ->method('getByEmail')
  363. ->with($user)
  364. ->willReturn([]);
  365. $this->urlGenerator->expects($this->once())
  366. ->method('linkToRoute')
  367. ->with('core.login.showLoginForm', [
  368. 'user' => 'MyUserName',
  369. 'redirect_url' => '/apps/files',
  370. ])
  371. ->will($this->returnValue($loginPageUrl));
  372. $this->userSession->expects($this->never())
  373. ->method('createSessionToken');
  374. $this->userSession->expects($this->never())
  375. ->method('createRememberMeToken');
  376. $this->config->expects($this->never())
  377. ->method('deleteUserValue');
  378. $expected = new \OCP\AppFramework\Http\RedirectResponse($loginPageUrl);
  379. $expected->throttle(['user' => 'MyUserName']);
  380. $this->assertEquals($expected, $this->loginController->tryLogin($user, $password, '/apps/files'));
  381. }
  382. public function testLoginWithValidCredentials() {
  383. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  384. $user = $this->createMock(IUser::class);
  385. $user->expects($this->any())
  386. ->method('getUID')
  387. ->will($this->returnValue('uid'));
  388. $loginName = 'loginli';
  389. $user->expects($this->any())
  390. ->method('getLastLogin')
  391. ->willReturn(123456);
  392. $password = 'secret';
  393. $indexPageUrl = \OC_Util::getDefaultPageUrl();
  394. $this->request
  395. ->expects($this->once())
  396. ->method('passesCSRFCheck')
  397. ->willReturn(true);
  398. $this->userManager->expects($this->once())
  399. ->method('checkPasswordNoLogging')
  400. ->will($this->returnValue($user));
  401. $this->userSession->expects($this->once())
  402. ->method('completeLogin')
  403. ->with($user, ['loginName' => $loginName, 'password' => $password]);
  404. $this->userSession->expects($this->once())
  405. ->method('createSessionToken')
  406. ->with($this->request, $user->getUID(), $loginName, $password, IToken::REMEMBER);
  407. $this->twoFactorManager->expects($this->once())
  408. ->method('isTwoFactorAuthenticated')
  409. ->with($user)
  410. ->will($this->returnValue(false));
  411. $this->config->expects($this->once())
  412. ->method('deleteUserValue')
  413. ->with('uid', 'core', 'lostpassword');
  414. $this->config->expects($this->once())
  415. ->method('setUserValue')
  416. ->with('uid', 'core', 'timezone', 'Europe/Berlin');
  417. $this->config
  418. ->method('getSystemValue')
  419. ->with('remember_login_cookie_lifetime')
  420. ->willReturn(1234);
  421. $this->userSession->expects($this->never())
  422. ->method('createRememberMeToken');
  423. $this->session->expects($this->exactly(2))
  424. ->method('set')
  425. ->withConsecutive(
  426. ['last-password-confirm', 123456],
  427. ['timezone', '1']
  428. );
  429. $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl);
  430. $this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, false, 'Europe/Berlin', '1'));
  431. }
  432. public function testLoginWithValidCredentialsAndRememberMe() {
  433. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  434. $user = $this->createMock(IUser::class);
  435. $user->expects($this->any())
  436. ->method('getUID')
  437. ->will($this->returnValue('uid'));
  438. $loginName = 'loginli';
  439. $password = 'secret';
  440. $indexPageUrl = \OC_Util::getDefaultPageUrl();
  441. $this->request
  442. ->expects($this->once())
  443. ->method('passesCSRFCheck')
  444. ->willReturn(true);
  445. $this->userManager->expects($this->once())
  446. ->method('checkPasswordNoLogging')
  447. ->will($this->returnValue($user));
  448. $this->userSession->expects($this->once())
  449. ->method('completeLogin')
  450. ->with($user, ['loginName' => $loginName, 'password' => $password]);
  451. $this->userSession->expects($this->once())
  452. ->method('createSessionToken')
  453. ->with($this->request, $user->getUID(), $loginName, $password, true);
  454. $this->twoFactorManager->expects($this->once())
  455. ->method('isTwoFactorAuthenticated')
  456. ->with($user)
  457. ->will($this->returnValue(false));
  458. $this->config->expects($this->once())
  459. ->method('deleteUserValue')
  460. ->with('uid', 'core', 'lostpassword');
  461. $this->config
  462. ->method('getSystemValue')
  463. ->with('remember_login_cookie_lifetime')
  464. ->willReturn(1234);
  465. $this->userSession->expects($this->once())
  466. ->method('createRememberMeToken')
  467. ->with($user);
  468. $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl);
  469. $this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, true));
  470. }
  471. public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() {
  472. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  473. $user = $this->createMock(IUser::class);
  474. $user->expects($this->any())
  475. ->method('getUID')
  476. ->will($this->returnValue('jane'));
  477. $password = 'secret';
  478. $originalUrl = 'another%20url';
  479. $this->request
  480. ->expects($this->once())
  481. ->method('passesCSRFCheck')
  482. ->willReturn(false);
  483. $this->userSession->expects($this->once())
  484. ->method('isLoggedIn')
  485. ->with()
  486. ->will($this->returnValue(false));
  487. $this->config->expects($this->never())
  488. ->method('deleteUserValue');
  489. $this->userSession->expects($this->never())
  490. ->method('createRememberMeToken');
  491. $expected = new \OCP\AppFramework\Http\RedirectResponse(\OC_Util::getDefaultPageUrl());
  492. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  493. }
  494. public function testLoginWithoutPassedCsrfCheckAndLoggedIn() {
  495. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  496. $user = $this->createMock(IUser::class);
  497. $user->expects($this->any())
  498. ->method('getUID')
  499. ->will($this->returnValue('jane'));
  500. $password = 'secret';
  501. $originalUrl = 'another%20url';
  502. $redirectUrl = 'http://localhost/another url';
  503. $this->request
  504. ->expects($this->once())
  505. ->method('passesCSRFCheck')
  506. ->willReturn(false);
  507. $this->userSession->expects($this->once())
  508. ->method('isLoggedIn')
  509. ->with()
  510. ->will($this->returnValue(true));
  511. $this->urlGenerator->expects($this->once())
  512. ->method('getAbsoluteURL')
  513. ->with(urldecode($originalUrl))
  514. ->will($this->returnValue($redirectUrl));
  515. $this->config->expects($this->never())
  516. ->method('deleteUserValue');
  517. $this->userSession->expects($this->never())
  518. ->method('createRememberMeToken');
  519. $this->config
  520. ->method('getSystemValue')
  521. ->with('remember_login_cookie_lifetime')
  522. ->willReturn(1234);
  523. $expected = new \OCP\AppFramework\Http\RedirectResponse($redirectUrl);
  524. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  525. }
  526. public function testLoginWithValidCredentialsAndRedirectUrl() {
  527. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  528. $user = $this->createMock(IUser::class);
  529. $user->expects($this->any())
  530. ->method('getUID')
  531. ->will($this->returnValue('jane'));
  532. $password = 'secret';
  533. $originalUrl = 'another%20url';
  534. $redirectUrl = 'http://localhost/another url';
  535. $this->request
  536. ->expects($this->once())
  537. ->method('passesCSRFCheck')
  538. ->willReturn(true);
  539. $this->userManager->expects($this->once())
  540. ->method('checkPasswordNoLogging')
  541. ->with('Jane', $password)
  542. ->will($this->returnValue($user));
  543. $this->userSession->expects($this->once())
  544. ->method('createSessionToken')
  545. ->with($this->request, $user->getUID(), 'Jane', $password, IToken::REMEMBER);
  546. $this->userSession->expects($this->once())
  547. ->method('isLoggedIn')
  548. ->with()
  549. ->will($this->returnValue(true));
  550. $this->urlGenerator->expects($this->once())
  551. ->method('getAbsoluteURL')
  552. ->with(urldecode($originalUrl))
  553. ->will($this->returnValue($redirectUrl));
  554. $this->config->expects($this->once())
  555. ->method('deleteUserValue')
  556. ->with('jane', 'core', 'lostpassword');
  557. $this->config
  558. ->method('getSystemValue')
  559. ->with('remember_login_cookie_lifetime')
  560. ->willReturn(1234);
  561. $expected = new \OCP\AppFramework\Http\RedirectResponse(urldecode($redirectUrl));
  562. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  563. }
  564. public function testLoginWithOneTwoFactorProvider() {
  565. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  566. $user = $this->createMock(IUser::class);
  567. $user->expects($this->any())
  568. ->method('getUID')
  569. ->will($this->returnValue('john'));
  570. $password = 'secret';
  571. $challengeUrl = 'challenge/url';
  572. $provider1 = $this->createMock(IProvider::class);
  573. $provider1->method('getId')->willReturn('u2f');
  574. $provider2 = $this->createMock(BackupCodesProvider::class);
  575. $provider2->method('getId')->willReturn('backup');
  576. $this->request
  577. ->expects($this->once())
  578. ->method('passesCSRFCheck')
  579. ->willReturn(true);
  580. $this->userManager->expects($this->once())
  581. ->method('checkPasswordNoLogging')
  582. ->will($this->returnValue($user));
  583. $this->userSession->expects($this->once())
  584. ->method('completeLogin')
  585. ->with($user, ['loginName' => 'john@doe.com', 'password' => $password]);
  586. $this->userSession->expects($this->once())
  587. ->method('createSessionToken')
  588. ->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER);
  589. $this->twoFactorManager->expects($this->once())
  590. ->method('isTwoFactorAuthenticated')
  591. ->with($user)
  592. ->will($this->returnValue(true));
  593. $this->twoFactorManager->expects($this->once())
  594. ->method('prepareTwoFactorLogin')
  595. ->with($user);
  596. $providerSet = new ProviderSet([$provider1, $provider2], false);
  597. $this->twoFactorManager->expects($this->once())
  598. ->method('getProviderSet')
  599. ->with($user)
  600. ->willReturn($providerSet);
  601. $this->urlGenerator->expects($this->once())
  602. ->method('linkToRoute')
  603. ->with('core.TwoFactorChallenge.showChallenge', [
  604. 'challengeProviderId' => 'u2f',
  605. ])
  606. ->will($this->returnValue($challengeUrl));
  607. $this->config->expects($this->once())
  608. ->method('deleteUserValue')
  609. ->with('john', 'core', 'lostpassword');
  610. $this->config
  611. ->method('getSystemValue')
  612. ->with('remember_login_cookie_lifetime')
  613. ->willReturn(1234);
  614. $this->userSession->expects($this->never())
  615. ->method('createRememberMeToken');
  616. $expected = new RedirectResponse($challengeUrl);
  617. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null));
  618. }
  619. public function testLoginWithMultipleTwoFactorProviders() {
  620. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  621. $user = $this->createMock(IUser::class);
  622. $user->expects($this->any())
  623. ->method('getUID')
  624. ->will($this->returnValue('john'));
  625. $password = 'secret';
  626. $challengeUrl = 'challenge/url';
  627. $provider1 = $this->createMock(IProvider::class);
  628. $provider2 = $this->createMock(IProvider::class);
  629. $provider1->method('getId')->willReturn('prov1');
  630. $provider2->method('getId')->willReturn('prov2');
  631. $this->request
  632. ->expects($this->once())
  633. ->method('passesCSRFCheck')
  634. ->willReturn(true);
  635. $this->userManager->expects($this->once())
  636. ->method('checkPasswordNoLogging')
  637. ->will($this->returnValue($user));
  638. $this->userSession->expects($this->once())
  639. ->method('completeLogin')
  640. ->with($user, ['loginName' => 'john@doe.com', 'password' => $password]);
  641. $this->userSession->expects($this->once())
  642. ->method('createSessionToken')
  643. ->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER);
  644. $this->twoFactorManager->expects($this->once())
  645. ->method('isTwoFactorAuthenticated')
  646. ->with($user)
  647. ->will($this->returnValue(true));
  648. $this->twoFactorManager->expects($this->once())
  649. ->method('prepareTwoFactorLogin')
  650. ->with($user);
  651. $providerSet = new ProviderSet([$provider1, $provider2], false);
  652. $this->twoFactorManager->expects($this->once())
  653. ->method('getProviderSet')
  654. ->with($user)
  655. ->willReturn($providerSet);
  656. $this->urlGenerator->expects($this->once())
  657. ->method('linkToRoute')
  658. ->with('core.TwoFactorChallenge.selectChallenge')
  659. ->will($this->returnValue($challengeUrl));
  660. $this->config->expects($this->once())
  661. ->method('deleteUserValue')
  662. ->with('john', 'core', 'lostpassword');
  663. $this->config
  664. ->method('getSystemValue')
  665. ->with('remember_login_cookie_lifetime')
  666. ->willReturn(1234);
  667. $this->userSession->expects($this->never())
  668. ->method('createRememberMeToken');
  669. $expected = new RedirectResponse($challengeUrl);
  670. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null));
  671. }
  672. public function testToNotLeakLoginName() {
  673. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  674. $user = $this->createMock(IUser::class);
  675. $user->expects($this->any())
  676. ->method('getUID')
  677. ->will($this->returnValue('john'));
  678. $this->userManager->expects($this->once())
  679. ->method('checkPasswordNoLogging')
  680. ->with('john@doe.com', 'just wrong')
  681. ->willReturn(false);
  682. $this->userManager->expects($this->once())
  683. ->method('checkPassword')
  684. ->with('john', 'just wrong')
  685. ->willReturn(false);
  686. $this->userManager->expects($this->once())
  687. ->method('getByEmail')
  688. ->with('john@doe.com')
  689. ->willReturn([$user]);
  690. $this->urlGenerator->expects($this->once())
  691. ->method('linkToRoute')
  692. ->with('core.login.showLoginForm', ['user' => 'john@doe.com'])
  693. ->will($this->returnValue(''));
  694. $this->request
  695. ->expects($this->once())
  696. ->method('passesCSRFCheck')
  697. ->willReturn(true);
  698. $this->config->expects($this->never())
  699. ->method('deleteUserValue');
  700. $this->userSession->expects($this->never())
  701. ->method('createRememberMeToken');
  702. $expected = new RedirectResponse('');
  703. $expected->throttle(['user' => 'john']);
  704. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null));
  705. }
  706. }