You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TwoFactorChallengeControllerTest.php 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. /**
  3. * @author Christoph Wurst <christoph@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 Test\Core\Controller;
  22. use OC\Authentication\TwoFactorAuth\Manager;
  23. use OC\Authentication\TwoFactorAuth\ProviderSet;
  24. use OC\Core\Controller\TwoFactorChallengeController;
  25. use OC_Util;
  26. use OCP\AppFramework\Http\RedirectResponse;
  27. use OCP\AppFramework\Http\StandaloneTemplateResponse;
  28. use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin;
  29. use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider;
  30. use OCP\Authentication\TwoFactorAuth\IProvider;
  31. use OCP\Authentication\TwoFactorAuth\TwoFactorException;
  32. use OCP\IRequest;
  33. use OCP\ISession;
  34. use OCP\IURLGenerator;
  35. use OCP\IUser;
  36. use OCP\IUserSession;
  37. use OCP\Template;
  38. use PHPUnit_Framework_MockObject_MockObject;
  39. use Test\TestCase;
  40. class TwoFactorChallengeControllerTest extends TestCase {
  41. /** @var IRequest|PHPUnit_Framework_MockObject_MockObject */
  42. private $request;
  43. /** @var Manager|PHPUnit_Framework_MockObject_MockObject */
  44. private $twoFactorManager;
  45. /** @var IUserSession|PHPUnit_Framework_MockObject_MockObject */
  46. private $userSession;
  47. /** @var ISession|PHPUnit_Framework_MockObject_MockObject */
  48. private $session;
  49. /** @var IURLGenerator|PHPUnit_Framework_MockObject_MockObject */
  50. private $urlGenerator;
  51. /** @var TwoFactorChallengeController|PHPUnit_Framework_MockObject_MockObject */
  52. private $controller;
  53. protected function setUp() {
  54. parent::setUp();
  55. $this->request = $this->createMock(IRequest::class);
  56. $this->twoFactorManager = $this->createMock(Manager::class);
  57. $this->userSession = $this->createMock(IUserSession::class);
  58. $this->session = $this->createMock(ISession::class);
  59. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  60. $this->controller = $this->getMockBuilder(TwoFactorChallengeController::class)
  61. ->setConstructorArgs([
  62. 'core',
  63. $this->request,
  64. $this->twoFactorManager,
  65. $this->userSession,
  66. $this->session,
  67. $this->urlGenerator,
  68. ])
  69. ->setMethods(['getLogoutUrl'])
  70. ->getMock();
  71. $this->controller->expects($this->any())
  72. ->method('getLogoutUrl')
  73. ->willReturn('logoutAttribute');
  74. }
  75. public function testSelectChallenge() {
  76. $user = $this->getMockBuilder(IUser::class)->getMock();
  77. $p1 = $this->createMock(IActivatableAtLogin::class);
  78. $p1->method('getId')->willReturn('p1');
  79. $backupProvider = $this->createMock(IProvider::class);
  80. $backupProvider->method('getId')->willReturn('backup_codes');
  81. $providerSet = new ProviderSet([$p1, $backupProvider], true);
  82. $this->twoFactorManager->expects($this->once())
  83. ->method('getLoginSetupProviders')
  84. ->with($user)
  85. ->willReturn([$p1]);
  86. $this->userSession->expects($this->once())
  87. ->method('getUser')
  88. ->will($this->returnValue($user));
  89. $this->twoFactorManager->expects($this->once())
  90. ->method('getProviderSet')
  91. ->with($user)
  92. ->will($this->returnValue($providerSet));
  93. $expected = new StandaloneTemplateResponse('core', 'twofactorselectchallenge', [
  94. 'providers' => [
  95. $p1,
  96. ],
  97. 'providerMissing' => true,
  98. 'backupProvider' => $backupProvider,
  99. 'redirect_url' => '/some/url',
  100. 'logout_url' => 'logoutAttribute',
  101. 'hasSetupProviders' => true,
  102. ], 'guest');
  103. $this->assertEquals($expected, $this->controller->selectChallenge('/some/url'));
  104. }
  105. public function testShowChallenge() {
  106. $user = $this->createMock(IUser::class);
  107. $provider = $this->createMock(IProvider::class);
  108. $provider->method('getId')->willReturn('myprovider');
  109. $backupProvider = $this->createMock(IProvider::class);
  110. $backupProvider->method('getId')->willReturn('backup_codes');
  111. $tmpl = $this->createMock(Template::class);
  112. $providerSet = new ProviderSet([$provider, $backupProvider], true);
  113. $this->userSession->expects($this->once())
  114. ->method('getUser')
  115. ->will($this->returnValue($user));
  116. $this->twoFactorManager->expects($this->once())
  117. ->method('getProviderSet')
  118. ->with($user)
  119. ->will($this->returnValue($providerSet));
  120. $provider->expects($this->once())
  121. ->method('getId')
  122. ->will($this->returnValue('u2f'));
  123. $backupProvider->expects($this->once())
  124. ->method('getId')
  125. ->will($this->returnValue('backup_codes'));
  126. $this->session->expects($this->once())
  127. ->method('exists')
  128. ->with('two_factor_auth_error')
  129. ->will($this->returnValue(true));
  130. $this->session->expects($this->exactly(2))
  131. ->method('remove')
  132. ->with($this->logicalOr($this->equalTo('two_factor_auth_error'), $this->equalTo('two_factor_auth_error_message')));
  133. $provider->expects($this->once())
  134. ->method('getTemplate')
  135. ->with($user)
  136. ->will($this->returnValue($tmpl));
  137. $tmpl->expects($this->once())
  138. ->method('fetchPage')
  139. ->will($this->returnValue('<html/>'));
  140. $expected = new StandaloneTemplateResponse('core', 'twofactorshowchallenge', [
  141. 'error' => true,
  142. 'provider' => $provider,
  143. 'backupProvider' => $backupProvider,
  144. 'logout_url' => 'logoutAttribute',
  145. 'template' => '<html/>',
  146. 'redirect_url' => '/re/dir/ect/url',
  147. 'error_message' => null,
  148. ], 'guest');
  149. $this->assertEquals($expected, $this->controller->showChallenge('myprovider', '/re/dir/ect/url'));
  150. }
  151. public function testShowInvalidChallenge() {
  152. $user = $this->createMock(IUser::class);
  153. $providerSet = new ProviderSet([], false);
  154. $this->userSession->expects($this->once())
  155. ->method('getUser')
  156. ->will($this->returnValue($user));
  157. $this->twoFactorManager->expects($this->once())
  158. ->method('getProviderSet')
  159. ->with($user)
  160. ->will($this->returnValue($providerSet));
  161. $this->urlGenerator->expects($this->once())
  162. ->method('linkToRoute')
  163. ->with('core.TwoFactorChallenge.selectChallenge')
  164. ->will($this->returnValue('select/challenge/url'));
  165. $expected = new RedirectResponse('select/challenge/url');
  166. $this->assertEquals($expected, $this->controller->showChallenge('myprovider', 'redirect/url'));
  167. }
  168. public function testSolveChallenge() {
  169. $user = $this->createMock(IUser::class);
  170. $provider = $this->createMock(IProvider::class);
  171. $this->userSession->expects($this->once())
  172. ->method('getUser')
  173. ->will($this->returnValue($user));
  174. $this->twoFactorManager->expects($this->once())
  175. ->method('getProvider')
  176. ->with($user, 'myprovider')
  177. ->will($this->returnValue($provider));
  178. $this->twoFactorManager->expects($this->once())
  179. ->method('verifyChallenge')
  180. ->with('myprovider', $user, 'token')
  181. ->will($this->returnValue(true));
  182. $expected = new RedirectResponse(OC_Util::getDefaultPageUrl());
  183. $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
  184. }
  185. public function testSolveValidChallengeAndRedirect() {
  186. $user = $this->createMock(IUser::class);
  187. $provider = $this->createMock(IProvider::class);
  188. $this->userSession->expects($this->once())
  189. ->method('getUser')
  190. ->will($this->returnValue($user));
  191. $this->twoFactorManager->expects($this->once())
  192. ->method('getProvider')
  193. ->with($user, 'myprovider')
  194. ->will($this->returnValue($provider));
  195. $this->twoFactorManager->expects($this->once())
  196. ->method('verifyChallenge')
  197. ->with('myprovider', $user, 'token')
  198. ->willReturn(true);
  199. $this->urlGenerator->expects($this->once())
  200. ->method('getAbsoluteURL')
  201. ->with('redirect url')
  202. ->willReturn('redirect/url');
  203. $expected = new RedirectResponse('redirect/url');
  204. $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', 'redirect%20url'));
  205. }
  206. public function testSolveChallengeInvalidProvider() {
  207. $user = $this->getMockBuilder(IUser::class)->getMock();
  208. $this->userSession->expects($this->once())
  209. ->method('getUser')
  210. ->will($this->returnValue($user));
  211. $this->twoFactorManager->expects($this->once())
  212. ->method('getProvider')
  213. ->with($user, 'myprovider')
  214. ->will($this->returnValue(null));
  215. $this->urlGenerator->expects($this->once())
  216. ->method('linkToRoute')
  217. ->with('core.TwoFactorChallenge.selectChallenge')
  218. ->will($this->returnValue('select/challenge/url'));
  219. $expected = new RedirectResponse('select/challenge/url');
  220. $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
  221. }
  222. public function testSolveInvalidChallenge() {
  223. $user = $this->createMock(IUser::class);
  224. $provider = $this->createMock(IProvider::class);
  225. $this->userSession->expects($this->once())
  226. ->method('getUser')
  227. ->will($this->returnValue($user));
  228. $this->twoFactorManager->expects($this->once())
  229. ->method('getProvider')
  230. ->with($user, 'myprovider')
  231. ->will($this->returnValue($provider));
  232. $this->twoFactorManager->expects($this->once())
  233. ->method('verifyChallenge')
  234. ->with('myprovider', $user, 'token')
  235. ->will($this->returnValue(false));
  236. $this->session->expects($this->once())
  237. ->method('set')
  238. ->with('two_factor_auth_error', true);
  239. $this->urlGenerator->expects($this->once())
  240. ->method('linkToRoute')
  241. ->with('core.TwoFactorChallenge.showChallenge', [
  242. 'challengeProviderId' => 'myprovider',
  243. 'redirect_url' => '/url',
  244. ])
  245. ->will($this->returnValue('files/index/url'));
  246. $provider->expects($this->once())
  247. ->method('getId')
  248. ->will($this->returnValue('myprovider'));
  249. $expected = new RedirectResponse('files/index/url');
  250. $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
  251. }
  252. public function testSolveChallengeTwoFactorException() {
  253. $user = $this->createMock(IUser::class);
  254. $provider = $this->createMock(IProvider::class);
  255. $exception = new TwoFactorException("2FA failed");
  256. $this->userSession->expects($this->once())
  257. ->method('getUser')
  258. ->will($this->returnValue($user));
  259. $this->twoFactorManager->expects($this->once())
  260. ->method('getProvider')
  261. ->with($user, 'myprovider')
  262. ->will($this->returnValue($provider));
  263. $this->twoFactorManager->expects($this->once())
  264. ->method('verifyChallenge')
  265. ->with('myprovider', $user, 'token')
  266. ->will($this->throwException($exception));
  267. $this->session->expects($this->at(0))
  268. ->method('set')
  269. ->with('two_factor_auth_error_message', "2FA failed");
  270. $this->session->expects($this->at(1))
  271. ->method('set')
  272. ->with('two_factor_auth_error', true);
  273. $this->urlGenerator->expects($this->once())
  274. ->method('linkToRoute')
  275. ->with('core.TwoFactorChallenge.showChallenge', [
  276. 'challengeProviderId' => 'myprovider',
  277. 'redirect_url' => '/url',
  278. ])
  279. ->will($this->returnValue('files/index/url'));
  280. $provider->expects($this->once())
  281. ->method('getId')
  282. ->will($this->returnValue('myprovider'));
  283. $expected = new RedirectResponse('files/index/url');
  284. $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
  285. }
  286. public function testSetUpProviders() {
  287. $user = $this->createMock(IUser::class);
  288. $this->userSession->expects($this->once())
  289. ->method('getUser')
  290. ->will($this->returnValue($user));
  291. $provider = $this->createMock(IActivatableAtLogin::class);
  292. $this->twoFactorManager->expects($this->once())
  293. ->method('getLoginSetupProviders')
  294. ->with($user)
  295. ->willReturn([
  296. $provider,
  297. ]);
  298. $expected = new StandaloneTemplateResponse(
  299. 'core',
  300. 'twofactorsetupselection',
  301. [
  302. 'providers' => [
  303. $provider,
  304. ],
  305. 'logout_url' => 'logoutAttribute',
  306. ],
  307. 'guest'
  308. );
  309. $response = $this->controller->setupProviders();
  310. $this->assertEquals($expected, $response);
  311. }
  312. public function testSetUpInvalidProvider() {
  313. $user = $this->createMock(IUser::class);
  314. $this->userSession->expects($this->once())
  315. ->method('getUser')
  316. ->will($this->returnValue($user));
  317. $provider = $this->createMock(IActivatableAtLogin::class);
  318. $provider->expects($this->any())
  319. ->method('getId')
  320. ->willReturn('prov1');
  321. $this->twoFactorManager->expects($this->once())
  322. ->method('getLoginSetupProviders')
  323. ->with($user)
  324. ->willReturn([
  325. $provider,
  326. ]);
  327. $this->urlGenerator->expects($this->once())
  328. ->method('linkToRoute')
  329. ->with('core.TwoFactorChallenge.selectChallenge')
  330. ->willReturn('2fa/select/page');
  331. $expected = new RedirectResponse('2fa/select/page');
  332. $response = $this->controller->setupProvider('prov2');
  333. $this->assertEquals($expected, $response);
  334. }
  335. public function testSetUpProvider() {
  336. $user = $this->createMock(IUser::class);
  337. $this->userSession->expects($this->once())
  338. ->method('getUser')
  339. ->will($this->returnValue($user));
  340. $provider = $this->createMock(IActivatableAtLogin::class);
  341. $provider->expects($this->any())
  342. ->method('getId')
  343. ->willReturn('prov1');
  344. $this->twoFactorManager->expects($this->once())
  345. ->method('getLoginSetupProviders')
  346. ->with($user)
  347. ->willReturn([
  348. $provider,
  349. ]);
  350. $loginSetup = $this->createMock(ILoginSetupProvider::class);
  351. $provider->expects($this->any())
  352. ->method('getLoginSetup')
  353. ->with($user)
  354. ->willReturn($loginSetup);
  355. $tmpl = $this->createMock(Template::class);
  356. $loginSetup->expects($this->once())
  357. ->method('getBody')
  358. ->willReturn($tmpl);
  359. $tmpl->expects($this->once())
  360. ->method('fetchPage')
  361. ->willReturn('tmpl');
  362. $expected = new StandaloneTemplateResponse(
  363. 'core',
  364. 'twofactorsetupchallenge',
  365. [
  366. 'provider' => $provider,
  367. 'logout_url' => 'logoutAttribute',
  368. 'template' => 'tmpl',
  369. ],
  370. 'guest'
  371. );
  372. $response = $this->controller->setupProvider('prov1');
  373. $this->assertEquals($expected, $response);
  374. }
  375. public function testConfirmProviderSetup() {
  376. $this->urlGenerator->expects($this->once())
  377. ->method('linkToRoute')
  378. ->with(
  379. 'core.TwoFactorChallenge.showChallenge',
  380. [
  381. 'challengeProviderId' => 'totp',
  382. ])
  383. ->willReturn('2fa/select/page');
  384. $expected = new RedirectResponse('2fa/select/page');
  385. $response = $this->controller->confirmProviderSetup('totp');
  386. $this->assertEquals($expected, $response);
  387. }
  388. }