LoginControllerTest.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  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", "executionContexts"');
  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", "executionContexts"');
  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->userSession->expects($this->never())
  418. ->method('createRememberMeToken');
  419. $this->session->expects($this->exactly(2))
  420. ->method('set')
  421. ->withConsecutive(
  422. ['last-password-confirm', 123456],
  423. ['timezone', '1']
  424. );
  425. $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl);
  426. $this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, false, 'Europe/Berlin', '1'));
  427. }
  428. public function testLoginWithValidCredentialsAndRememberMe() {
  429. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  430. $user = $this->createMock(IUser::class);
  431. $user->expects($this->any())
  432. ->method('getUID')
  433. ->will($this->returnValue('uid'));
  434. $loginName = 'loginli';
  435. $password = 'secret';
  436. $indexPageUrl = \OC_Util::getDefaultPageUrl();
  437. $this->request
  438. ->expects($this->once())
  439. ->method('passesCSRFCheck')
  440. ->willReturn(true);
  441. $this->userManager->expects($this->once())
  442. ->method('checkPasswordNoLogging')
  443. ->will($this->returnValue($user));
  444. $this->userSession->expects($this->once())
  445. ->method('completeLogin')
  446. ->with($user, ['loginName' => $loginName, 'password' => $password]);
  447. $this->userSession->expects($this->once())
  448. ->method('createSessionToken')
  449. ->with($this->request, $user->getUID(), $loginName, $password, true);
  450. $this->twoFactorManager->expects($this->once())
  451. ->method('isTwoFactorAuthenticated')
  452. ->with($user)
  453. ->will($this->returnValue(false));
  454. $this->config->expects($this->once())
  455. ->method('deleteUserValue')
  456. ->with('uid', 'core', 'lostpassword');
  457. $this->userSession->expects($this->once())
  458. ->method('createRememberMeToken')
  459. ->with($user);
  460. $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl);
  461. $this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, true));
  462. }
  463. public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() {
  464. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  465. $user = $this->createMock(IUser::class);
  466. $user->expects($this->any())
  467. ->method('getUID')
  468. ->will($this->returnValue('jane'));
  469. $password = 'secret';
  470. $originalUrl = 'another%20url';
  471. $this->request
  472. ->expects($this->once())
  473. ->method('passesCSRFCheck')
  474. ->willReturn(false);
  475. $this->userSession->expects($this->once())
  476. ->method('isLoggedIn')
  477. ->with()
  478. ->will($this->returnValue(false));
  479. $this->config->expects($this->never())
  480. ->method('deleteUserValue');
  481. $this->userSession->expects($this->never())
  482. ->method('createRememberMeToken');
  483. $expected = new \OCP\AppFramework\Http\RedirectResponse(\OC_Util::getDefaultPageUrl());
  484. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  485. }
  486. public function testLoginWithoutPassedCsrfCheckAndLoggedIn() {
  487. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  488. $user = $this->createMock(IUser::class);
  489. $user->expects($this->any())
  490. ->method('getUID')
  491. ->will($this->returnValue('jane'));
  492. $password = 'secret';
  493. $originalUrl = 'another%20url';
  494. $redirectUrl = 'http://localhost/another url';
  495. $this->request
  496. ->expects($this->once())
  497. ->method('passesCSRFCheck')
  498. ->willReturn(false);
  499. $this->userSession->expects($this->once())
  500. ->method('isLoggedIn')
  501. ->with()
  502. ->will($this->returnValue(true));
  503. $this->urlGenerator->expects($this->once())
  504. ->method('getAbsoluteURL')
  505. ->with(urldecode($originalUrl))
  506. ->will($this->returnValue($redirectUrl));
  507. $this->config->expects($this->never())
  508. ->method('deleteUserValue');
  509. $this->userSession->expects($this->never())
  510. ->method('createRememberMeToken');
  511. $expected = new \OCP\AppFramework\Http\RedirectResponse($redirectUrl);
  512. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  513. }
  514. public function testLoginWithValidCredentialsAndRedirectUrl() {
  515. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  516. $user = $this->createMock(IUser::class);
  517. $user->expects($this->any())
  518. ->method('getUID')
  519. ->will($this->returnValue('jane'));
  520. $password = 'secret';
  521. $originalUrl = 'another%20url';
  522. $redirectUrl = 'http://localhost/another url';
  523. $this->request
  524. ->expects($this->once())
  525. ->method('passesCSRFCheck')
  526. ->willReturn(true);
  527. $this->userManager->expects($this->once())
  528. ->method('checkPasswordNoLogging')
  529. ->with('Jane', $password)
  530. ->will($this->returnValue($user));
  531. $this->userSession->expects($this->once())
  532. ->method('createSessionToken')
  533. ->with($this->request, $user->getUID(), 'Jane', $password, IToken::REMEMBER);
  534. $this->userSession->expects($this->once())
  535. ->method('isLoggedIn')
  536. ->with()
  537. ->will($this->returnValue(true));
  538. $this->urlGenerator->expects($this->once())
  539. ->method('getAbsoluteURL')
  540. ->with(urldecode($originalUrl))
  541. ->will($this->returnValue($redirectUrl));
  542. $this->config->expects($this->once())
  543. ->method('deleteUserValue')
  544. ->with('jane', 'core', 'lostpassword');
  545. $expected = new \OCP\AppFramework\Http\RedirectResponse(urldecode($redirectUrl));
  546. $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
  547. }
  548. public function testLoginWithOneTwoFactorProvider() {
  549. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  550. $user = $this->createMock(IUser::class);
  551. $user->expects($this->any())
  552. ->method('getUID')
  553. ->will($this->returnValue('john'));
  554. $password = 'secret';
  555. $challengeUrl = 'challenge/url';
  556. $provider1 = $this->createMock(IProvider::class);
  557. $provider1->method('getId')->willReturn('u2f');
  558. $provider2 = $this->createMock(BackupCodesProvider::class);
  559. $provider2->method('getId')->willReturn('backup');
  560. $this->request
  561. ->expects($this->once())
  562. ->method('passesCSRFCheck')
  563. ->willReturn(true);
  564. $this->userManager->expects($this->once())
  565. ->method('checkPasswordNoLogging')
  566. ->will($this->returnValue($user));
  567. $this->userSession->expects($this->once())
  568. ->method('completeLogin')
  569. ->with($user, ['loginName' => 'john@doe.com', 'password' => $password]);
  570. $this->userSession->expects($this->once())
  571. ->method('createSessionToken')
  572. ->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER);
  573. $this->twoFactorManager->expects($this->once())
  574. ->method('isTwoFactorAuthenticated')
  575. ->with($user)
  576. ->will($this->returnValue(true));
  577. $this->twoFactorManager->expects($this->once())
  578. ->method('prepareTwoFactorLogin')
  579. ->with($user);
  580. $providerSet = new ProviderSet([$provider1, $provider2], false);
  581. $this->twoFactorManager->expects($this->once())
  582. ->method('getProviderSet')
  583. ->with($user)
  584. ->willReturn($providerSet);
  585. $this->urlGenerator->expects($this->once())
  586. ->method('linkToRoute')
  587. ->with('core.TwoFactorChallenge.showChallenge', [
  588. 'challengeProviderId' => 'u2f',
  589. ])
  590. ->will($this->returnValue($challengeUrl));
  591. $this->config->expects($this->once())
  592. ->method('deleteUserValue')
  593. ->with('john', 'core', 'lostpassword');
  594. $this->userSession->expects($this->never())
  595. ->method('createRememberMeToken');
  596. $expected = new RedirectResponse($challengeUrl);
  597. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null));
  598. }
  599. public function testLoginWithMultipleTwoFactorProviders() {
  600. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  601. $user = $this->createMock(IUser::class);
  602. $user->expects($this->any())
  603. ->method('getUID')
  604. ->will($this->returnValue('john'));
  605. $password = 'secret';
  606. $challengeUrl = 'challenge/url';
  607. $provider1 = $this->createMock(IProvider::class);
  608. $provider2 = $this->createMock(IProvider::class);
  609. $provider1->method('getId')->willReturn('prov1');
  610. $provider2->method('getId')->willReturn('prov2');
  611. $this->request
  612. ->expects($this->once())
  613. ->method('passesCSRFCheck')
  614. ->willReturn(true);
  615. $this->userManager->expects($this->once())
  616. ->method('checkPasswordNoLogging')
  617. ->will($this->returnValue($user));
  618. $this->userSession->expects($this->once())
  619. ->method('completeLogin')
  620. ->with($user, ['loginName' => 'john@doe.com', 'password' => $password]);
  621. $this->userSession->expects($this->once())
  622. ->method('createSessionToken')
  623. ->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER);
  624. $this->twoFactorManager->expects($this->once())
  625. ->method('isTwoFactorAuthenticated')
  626. ->with($user)
  627. ->will($this->returnValue(true));
  628. $this->twoFactorManager->expects($this->once())
  629. ->method('prepareTwoFactorLogin')
  630. ->with($user);
  631. $providerSet = new ProviderSet([$provider1, $provider2], false);
  632. $this->twoFactorManager->expects($this->once())
  633. ->method('getProviderSet')
  634. ->with($user)
  635. ->willReturn($providerSet);
  636. $this->urlGenerator->expects($this->once())
  637. ->method('linkToRoute')
  638. ->with('core.TwoFactorChallenge.selectChallenge')
  639. ->will($this->returnValue($challengeUrl));
  640. $this->config->expects($this->once())
  641. ->method('deleteUserValue')
  642. ->with('john', 'core', 'lostpassword');
  643. $this->userSession->expects($this->never())
  644. ->method('createRememberMeToken');
  645. $expected = new RedirectResponse($challengeUrl);
  646. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null));
  647. }
  648. public function testToNotLeakLoginName() {
  649. /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
  650. $user = $this->createMock(IUser::class);
  651. $user->expects($this->any())
  652. ->method('getUID')
  653. ->will($this->returnValue('john'));
  654. $this->userManager->expects($this->once())
  655. ->method('checkPasswordNoLogging')
  656. ->with('john@doe.com', 'just wrong')
  657. ->willReturn(false);
  658. $this->userManager->expects($this->once())
  659. ->method('checkPassword')
  660. ->with('john', 'just wrong')
  661. ->willReturn(false);
  662. $this->userManager->expects($this->once())
  663. ->method('getByEmail')
  664. ->with('john@doe.com')
  665. ->willReturn([$user]);
  666. $this->urlGenerator->expects($this->once())
  667. ->method('linkToRoute')
  668. ->with('core.login.showLoginForm', ['user' => 'john@doe.com'])
  669. ->will($this->returnValue(''));
  670. $this->request
  671. ->expects($this->once())
  672. ->method('passesCSRFCheck')
  673. ->willReturn(true);
  674. $this->config->expects($this->never())
  675. ->method('deleteUserValue');
  676. $this->userSession->expects($this->never())
  677. ->method('createRememberMeToken');
  678. $expected = new RedirectResponse('');
  679. $expected->throttle(['user' => 'john']);
  680. $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null));
  681. }
  682. }