ClientFlowLoginControllerTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  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
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace Tests\Core\Controller;
  22. use OC\Authentication\Exceptions\InvalidTokenException;
  23. use OC\Authentication\Exceptions\PasswordlessTokenException;
  24. use OC\Authentication\Token\IProvider;
  25. use OC\Authentication\Token\IToken;
  26. use OC\Core\Controller\ClientFlowLoginController;
  27. use OCA\OAuth2\Db\AccessTokenMapper;
  28. use OCA\OAuth2\Db\Client;
  29. use OCA\OAuth2\Db\ClientMapper;
  30. use OCP\AppFramework\Http;
  31. use OCP\AppFramework\Http\StandaloneTemplateResponse;
  32. use OCP\Defaults;
  33. use OCP\IL10N;
  34. use OCP\IRequest;
  35. use OCP\ISession;
  36. use OCP\IURLGenerator;
  37. use OCP\IUser;
  38. use OCP\IUserSession;
  39. use OCP\Security\ICrypto;
  40. use OCP\Security\ISecureRandom;
  41. use OCP\Session\Exceptions\SessionNotAvailableException;
  42. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  43. use Test\TestCase;
  44. class ClientFlowLoginControllerTest extends TestCase {
  45. /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
  46. private $request;
  47. /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
  48. private $userSession;
  49. /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
  50. private $l10n;
  51. /** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */
  52. private $defaults;
  53. /** @var ISession|\PHPUnit_Framework_MockObject_MockObject */
  54. private $session;
  55. /** @var IProvider|\PHPUnit_Framework_MockObject_MockObject */
  56. private $tokenProvider;
  57. /** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */
  58. private $random;
  59. /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
  60. private $urlGenerator;
  61. /** @var ClientMapper|\PHPUnit_Framework_MockObject_MockObject */
  62. private $clientMapper;
  63. /** @var AccessTokenMapper|\PHPUnit_Framework_MockObject_MockObject */
  64. private $accessTokenMapper;
  65. /** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */
  66. private $crypto;
  67. /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
  68. private $eventDispatcher;
  69. /** @var ClientFlowLoginController */
  70. private $clientFlowLoginController;
  71. public function setUp() {
  72. parent::setUp();
  73. $this->request = $this->createMock(IRequest::class);
  74. $this->userSession = $this->createMock(IUserSession::class);
  75. $this->l10n = $this->createMock(IL10N::class);
  76. $this->l10n
  77. ->expects($this->any())
  78. ->method('t')
  79. ->will($this->returnCallback(function($text, $parameters = array()) {
  80. return vsprintf($text, $parameters);
  81. }));
  82. $this->defaults = $this->createMock(Defaults::class);
  83. $this->session = $this->createMock(ISession::class);
  84. $this->tokenProvider = $this->createMock(IProvider::class);
  85. $this->random = $this->createMock(ISecureRandom::class);
  86. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  87. $this->clientMapper = $this->createMock(ClientMapper::class);
  88. $this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
  89. $this->crypto = $this->createMock(ICrypto::class);
  90. $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
  91. $this->clientFlowLoginController = new ClientFlowLoginController(
  92. 'core',
  93. $this->request,
  94. $this->userSession,
  95. $this->l10n,
  96. $this->defaults,
  97. $this->session,
  98. $this->tokenProvider,
  99. $this->random,
  100. $this->urlGenerator,
  101. $this->clientMapper,
  102. $this->accessTokenMapper,
  103. $this->crypto,
  104. $this->eventDispatcher
  105. );
  106. }
  107. public function testShowAuthPickerPageNoClientOrOauthRequest() {
  108. $expected = new StandaloneTemplateResponse(
  109. 'core',
  110. 'error',
  111. [
  112. 'errors' =>
  113. [
  114. [
  115. 'error' => 'Access Forbidden',
  116. 'hint' => 'Invalid request',
  117. ],
  118. ],
  119. ],
  120. 'guest'
  121. );
  122. $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage());
  123. }
  124. public function testShowAuthPickerPageWithOcsHeader() {
  125. $this->request
  126. ->expects($this->at(0))
  127. ->method('getHeader')
  128. ->with('USER_AGENT')
  129. ->willReturn('Mac OS X Sync Client');
  130. $this->request
  131. ->expects($this->at(1))
  132. ->method('getHeader')
  133. ->with('OCS-APIREQUEST')
  134. ->willReturn('true');
  135. $this->random
  136. ->expects($this->once())
  137. ->method('generate')
  138. ->with(
  139. 64,
  140. ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
  141. )
  142. ->willReturn('StateToken');
  143. $this->session
  144. ->expects($this->once())
  145. ->method('set')
  146. ->with('client.flow.state.token', 'StateToken');
  147. $this->session
  148. ->expects($this->once())
  149. ->method('get')
  150. ->with('oauth.state')
  151. ->willReturn('OauthStateToken');
  152. $this->defaults
  153. ->expects($this->once())
  154. ->method('getName')
  155. ->willReturn('ExampleCloud');
  156. $this->request
  157. ->expects($this->once())
  158. ->method('getServerHost')
  159. ->willReturn('example.com');
  160. $this->request
  161. ->method('getServerProtocol')
  162. ->willReturn('https');
  163. $expected = new StandaloneTemplateResponse(
  164. 'core',
  165. 'loginflow/authpicker',
  166. [
  167. 'client' => 'Mac OS X Sync Client',
  168. 'clientIdentifier' => '',
  169. 'instanceName' => 'ExampleCloud',
  170. 'urlGenerator' => $this->urlGenerator,
  171. 'stateToken' => 'StateToken',
  172. 'serverHost' => 'https://example.com',
  173. 'oauthState' => 'OauthStateToken',
  174. ],
  175. 'guest'
  176. );
  177. $csp = new Http\ContentSecurityPolicy();
  178. $csp->addAllowedFormActionDomain('nc://*');
  179. $expected->setContentSecurityPolicy($csp);
  180. $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage());
  181. }
  182. public function testShowAuthPickerPageWithOauth() {
  183. $this->request
  184. ->expects($this->at(0))
  185. ->method('getHeader')
  186. ->with('USER_AGENT')
  187. ->willReturn('Mac OS X Sync Client');
  188. $client = new Client();
  189. $client->setName('My external service');
  190. $this->clientMapper
  191. ->expects($this->once())
  192. ->method('getByIdentifier')
  193. ->with('MyClientIdentifier')
  194. ->willReturn($client);
  195. $this->random
  196. ->expects($this->once())
  197. ->method('generate')
  198. ->with(
  199. 64,
  200. ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
  201. )
  202. ->willReturn('StateToken');
  203. $this->session
  204. ->expects($this->once())
  205. ->method('set')
  206. ->with('client.flow.state.token', 'StateToken');
  207. $this->session
  208. ->expects($this->once())
  209. ->method('get')
  210. ->with('oauth.state')
  211. ->willReturn('OauthStateToken');
  212. $this->defaults
  213. ->expects($this->once())
  214. ->method('getName')
  215. ->willReturn('ExampleCloud');
  216. $this->request
  217. ->expects($this->once())
  218. ->method('getServerHost')
  219. ->willReturn('example.com');
  220. $this->request
  221. ->method('getServerProtocol')
  222. ->willReturn('https');
  223. $expected = new StandaloneTemplateResponse(
  224. 'core',
  225. 'loginflow/authpicker',
  226. [
  227. 'client' => 'My external service',
  228. 'clientIdentifier' => 'MyClientIdentifier',
  229. 'instanceName' => 'ExampleCloud',
  230. 'urlGenerator' => $this->urlGenerator,
  231. 'stateToken' => 'StateToken',
  232. 'serverHost' => 'https://example.com',
  233. 'oauthState' => 'OauthStateToken',
  234. ],
  235. 'guest'
  236. );
  237. $csp = new Http\ContentSecurityPolicy();
  238. $csp->addAllowedFormActionDomain('nc://*');
  239. $expected->setContentSecurityPolicy($csp);
  240. $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage('MyClientIdentifier'));
  241. }
  242. public function testGenerateAppPasswordWithInvalidToken() {
  243. $this->session
  244. ->expects($this->once())
  245. ->method('get')
  246. ->with('client.flow.state.token')
  247. ->willReturn('OtherToken');
  248. $this->session
  249. ->expects($this->once())
  250. ->method('remove')
  251. ->with('client.flow.state.token');
  252. $expected = new StandaloneTemplateResponse(
  253. 'core',
  254. '403',
  255. [
  256. 'message' => 'State token does not match',
  257. ],
  258. 'guest'
  259. );
  260. $expected->setStatus(Http::STATUS_FORBIDDEN);
  261. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  262. }
  263. public function testGenerateAppPasswordWithSessionNotAvailableException() {
  264. $this->session
  265. ->expects($this->once())
  266. ->method('get')
  267. ->with('client.flow.state.token')
  268. ->willReturn('MyStateToken');
  269. $this->session
  270. ->expects($this->once())
  271. ->method('remove')
  272. ->with('client.flow.state.token');
  273. $this->session
  274. ->expects($this->once())
  275. ->method('getId')
  276. ->willThrowException(new SessionNotAvailableException());
  277. $expected = new Http\Response();
  278. $expected->setStatus(Http::STATUS_FORBIDDEN);
  279. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  280. }
  281. public function testGenerateAppPasswordWithInvalidTokenException() {
  282. $this->session
  283. ->expects($this->once())
  284. ->method('get')
  285. ->with('client.flow.state.token')
  286. ->willReturn('MyStateToken');
  287. $this->session
  288. ->expects($this->once())
  289. ->method('remove')
  290. ->with('client.flow.state.token');
  291. $this->session
  292. ->expects($this->once())
  293. ->method('getId')
  294. ->willReturn('SessionId');
  295. $this->tokenProvider
  296. ->expects($this->once())
  297. ->method('getToken')
  298. ->with('SessionId')
  299. ->willThrowException(new InvalidTokenException());
  300. $expected = new Http\Response();
  301. $expected->setStatus(Http::STATUS_FORBIDDEN);
  302. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  303. }
  304. public function testGeneratePasswordWithPassword() {
  305. $this->session
  306. ->expects($this->once())
  307. ->method('get')
  308. ->with('client.flow.state.token')
  309. ->willReturn('MyStateToken');
  310. $this->session
  311. ->expects($this->once())
  312. ->method('remove')
  313. ->with('client.flow.state.token');
  314. $this->session
  315. ->expects($this->once())
  316. ->method('getId')
  317. ->willReturn('SessionId');
  318. $myToken = $this->createMock(IToken::class);
  319. $myToken
  320. ->expects($this->once())
  321. ->method('getLoginName')
  322. ->willReturn('MyLoginName');
  323. $this->tokenProvider
  324. ->expects($this->once())
  325. ->method('getToken')
  326. ->with('SessionId')
  327. ->willReturn($myToken);
  328. $this->tokenProvider
  329. ->expects($this->once())
  330. ->method('getPassword')
  331. ->with($myToken, 'SessionId')
  332. ->willReturn('MyPassword');
  333. $this->random
  334. ->expects($this->once())
  335. ->method('generate')
  336. ->with(72)
  337. ->willReturn('MyGeneratedToken');
  338. $user = $this->createMock(IUser::class);
  339. $user
  340. ->expects($this->once())
  341. ->method('getUID')
  342. ->willReturn('MyUid');
  343. $this->userSession
  344. ->expects($this->once())
  345. ->method('getUser')
  346. ->willReturn($user);
  347. $this->tokenProvider
  348. ->expects($this->once())
  349. ->method('generateToken')
  350. ->with(
  351. 'MyGeneratedToken',
  352. 'MyUid',
  353. 'MyLoginName',
  354. 'MyPassword',
  355. 'unknown',
  356. IToken::PERMANENT_TOKEN,
  357. IToken::DO_NOT_REMEMBER
  358. );
  359. $this->request
  360. ->expects($this->once())
  361. ->method('getServerProtocol')
  362. ->willReturn('http');
  363. $this->request
  364. ->expects($this->once())
  365. ->method('getServerHost')
  366. ->willReturn('example.com');
  367. $this->request
  368. ->expects($this->any())
  369. ->method('getHeader')
  370. ->willReturn('');
  371. $this->eventDispatcher->expects($this->once())
  372. ->method('dispatch');
  373. $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
  374. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  375. }
  376. public function testGeneratePasswordWithPasswordForOauthClient() {
  377. $this->session
  378. ->expects($this->at(0))
  379. ->method('get')
  380. ->with('client.flow.state.token')
  381. ->willReturn('MyStateToken');
  382. $this->session
  383. ->expects($this->at(1))
  384. ->method('remove')
  385. ->with('client.flow.state.token');
  386. $this->session
  387. ->expects($this->at(3))
  388. ->method('get')
  389. ->with('oauth.state')
  390. ->willReturn('MyOauthState');
  391. $this->session
  392. ->expects($this->at(4))
  393. ->method('remove')
  394. ->with('oauth.state');
  395. $this->session
  396. ->expects($this->once())
  397. ->method('getId')
  398. ->willReturn('SessionId');
  399. $myToken = $this->createMock(IToken::class);
  400. $myToken
  401. ->expects($this->once())
  402. ->method('getLoginName')
  403. ->willReturn('MyLoginName');
  404. $this->tokenProvider
  405. ->expects($this->once())
  406. ->method('getToken')
  407. ->with('SessionId')
  408. ->willReturn($myToken);
  409. $this->tokenProvider
  410. ->expects($this->once())
  411. ->method('getPassword')
  412. ->with($myToken, 'SessionId')
  413. ->willReturn('MyPassword');
  414. $this->random
  415. ->expects($this->at(0))
  416. ->method('generate')
  417. ->with(72)
  418. ->willReturn('MyGeneratedToken');
  419. $this->random
  420. ->expects($this->at(1))
  421. ->method('generate')
  422. ->with(128)
  423. ->willReturn('MyAccessCode');
  424. $user = $this->createMock(IUser::class);
  425. $user
  426. ->expects($this->once())
  427. ->method('getUID')
  428. ->willReturn('MyUid');
  429. $this->userSession
  430. ->expects($this->once())
  431. ->method('getUser')
  432. ->willReturn($user);
  433. $token = $this->createMock(IToken::class);
  434. $this->tokenProvider
  435. ->expects($this->once())
  436. ->method('generateToken')
  437. ->with(
  438. 'MyGeneratedToken',
  439. 'MyUid',
  440. 'MyLoginName',
  441. 'MyPassword',
  442. 'My OAuth client',
  443. IToken::PERMANENT_TOKEN,
  444. IToken::DO_NOT_REMEMBER
  445. )
  446. ->willReturn($token);
  447. $client = new Client();
  448. $client->setName('My OAuth client');
  449. $client->setRedirectUri('https://example.com/redirect.php');
  450. $this->clientMapper
  451. ->expects($this->once())
  452. ->method('getByIdentifier')
  453. ->with('MyClientIdentifier')
  454. ->willReturn($client);
  455. $this->eventDispatcher->expects($this->once())
  456. ->method('dispatch');
  457. $expected = new Http\RedirectResponse('https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode');
  458. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier'));
  459. }
  460. public function testGeneratePasswordWithoutPassword() {
  461. $this->session
  462. ->expects($this->once())
  463. ->method('get')
  464. ->with('client.flow.state.token')
  465. ->willReturn('MyStateToken');
  466. $this->session
  467. ->expects($this->once())
  468. ->method('remove')
  469. ->with('client.flow.state.token');
  470. $this->session
  471. ->expects($this->once())
  472. ->method('getId')
  473. ->willReturn('SessionId');
  474. $myToken = $this->createMock(IToken::class);
  475. $myToken
  476. ->expects($this->once())
  477. ->method('getLoginName')
  478. ->willReturn('MyLoginName');
  479. $this->tokenProvider
  480. ->expects($this->once())
  481. ->method('getToken')
  482. ->with('SessionId')
  483. ->willReturn($myToken);
  484. $this->tokenProvider
  485. ->expects($this->once())
  486. ->method('getPassword')
  487. ->with($myToken, 'SessionId')
  488. ->willThrowException(new PasswordlessTokenException());
  489. $this->random
  490. ->expects($this->once())
  491. ->method('generate')
  492. ->with(72)
  493. ->willReturn('MyGeneratedToken');
  494. $user = $this->createMock(IUser::class);
  495. $user
  496. ->expects($this->once())
  497. ->method('getUID')
  498. ->willReturn('MyUid');
  499. $this->userSession
  500. ->expects($this->once())
  501. ->method('getUser')
  502. ->willReturn($user);
  503. $this->tokenProvider
  504. ->expects($this->once())
  505. ->method('generateToken')
  506. ->with(
  507. 'MyGeneratedToken',
  508. 'MyUid',
  509. 'MyLoginName',
  510. null,
  511. 'unknown',
  512. IToken::PERMANENT_TOKEN,
  513. IToken::DO_NOT_REMEMBER
  514. );
  515. $this->request
  516. ->expects($this->once())
  517. ->method('getServerProtocol')
  518. ->willReturn('http');
  519. $this->request
  520. ->expects($this->once())
  521. ->method('getServerHost')
  522. ->willReturn('example.com');
  523. $this->request
  524. ->expects($this->any())
  525. ->method('getHeader')
  526. ->willReturn('');
  527. $this->eventDispatcher->expects($this->once())
  528. ->method('dispatch');
  529. $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
  530. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  531. }
  532. public function dataGeneratePasswordWithHttpsProxy() {
  533. return [
  534. [
  535. [
  536. ['X-Forwarded-Proto', 'http'],
  537. ['X-Forwarded-Ssl', 'off'],
  538. ['USER_AGENT', ''],
  539. ],
  540. 'http',
  541. 'http',
  542. ],
  543. [
  544. [
  545. ['X-Forwarded-Proto', 'http'],
  546. ['X-Forwarded-Ssl', 'off'],
  547. ['USER_AGENT', ''],
  548. ],
  549. 'https',
  550. 'https',
  551. ],
  552. [
  553. [
  554. ['X-Forwarded-Proto', 'https'],
  555. ['X-Forwarded-Ssl', 'off'],
  556. ['USER_AGENT', ''],
  557. ],
  558. 'http',
  559. 'https',
  560. ],
  561. [
  562. [
  563. ['X-Forwarded-Proto', 'https'],
  564. ['X-Forwarded-Ssl', 'on'],
  565. ['USER_AGENT', ''],
  566. ],
  567. 'http',
  568. 'https',
  569. ],
  570. [
  571. [
  572. ['X-Forwarded-Proto', 'http'],
  573. ['X-Forwarded-Ssl', 'on'],
  574. ['USER_AGENT', ''],
  575. ],
  576. 'http',
  577. 'https',
  578. ],
  579. ];
  580. }
  581. /**
  582. * @dataProvider dataGeneratePasswordWithHttpsProxy
  583. * @param array $headers
  584. * @param string $protocol
  585. * @param string $expected
  586. */
  587. public function testGeneratePasswordWithHttpsProxy(array $headers, $protocol, $expected) {
  588. $this->session
  589. ->expects($this->once())
  590. ->method('get')
  591. ->with('client.flow.state.token')
  592. ->willReturn('MyStateToken');
  593. $this->session
  594. ->expects($this->once())
  595. ->method('remove')
  596. ->with('client.flow.state.token');
  597. $this->session
  598. ->expects($this->once())
  599. ->method('getId')
  600. ->willReturn('SessionId');
  601. $myToken = $this->createMock(IToken::class);
  602. $myToken
  603. ->expects($this->once())
  604. ->method('getLoginName')
  605. ->willReturn('MyLoginName');
  606. $this->tokenProvider
  607. ->expects($this->once())
  608. ->method('getToken')
  609. ->with('SessionId')
  610. ->willReturn($myToken);
  611. $this->tokenProvider
  612. ->expects($this->once())
  613. ->method('getPassword')
  614. ->with($myToken, 'SessionId')
  615. ->willReturn('MyPassword');
  616. $this->random
  617. ->expects($this->once())
  618. ->method('generate')
  619. ->with(72)
  620. ->willReturn('MyGeneratedToken');
  621. $user = $this->createMock(IUser::class);
  622. $user
  623. ->expects($this->once())
  624. ->method('getUID')
  625. ->willReturn('MyUid');
  626. $this->userSession
  627. ->expects($this->once())
  628. ->method('getUser')
  629. ->willReturn($user);
  630. $this->tokenProvider
  631. ->expects($this->once())
  632. ->method('generateToken')
  633. ->with(
  634. 'MyGeneratedToken',
  635. 'MyUid',
  636. 'MyLoginName',
  637. 'MyPassword',
  638. 'unknown',
  639. IToken::PERMANENT_TOKEN,
  640. IToken::DO_NOT_REMEMBER
  641. );
  642. $this->request
  643. ->expects($this->once())
  644. ->method('getServerProtocol')
  645. ->willReturn($protocol);
  646. $this->request
  647. ->expects($this->once())
  648. ->method('getServerHost')
  649. ->willReturn('example.com');
  650. $this->request
  651. ->expects($this->atLeastOnce())
  652. ->method('getHeader')
  653. ->willReturnMap($headers);
  654. $this->eventDispatcher->expects($this->once())
  655. ->method('dispatch');
  656. $expected = new Http\RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken');
  657. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  658. }
  659. }