ClientFlowLoginControllerTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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\EventDispatcher\IEventDispatcher;
  34. use OCP\IL10N;
  35. use OCP\IRequest;
  36. use OCP\ISession;
  37. use OCP\IURLGenerator;
  38. use OCP\IUser;
  39. use OCP\IUserSession;
  40. use OCP\Security\ICrypto;
  41. use OCP\Security\ISecureRandom;
  42. use OCP\Session\Exceptions\SessionNotAvailableException;
  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 IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
  68. private $eventDispatcher;
  69. /** @var ClientFlowLoginController */
  70. private $clientFlowLoginController;
  71. protected function setUp(): void {
  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. ->willReturnCallback(function ($text, $parameters = []) {
  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(IEventDispatcher::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. ->method('getHeader')
  127. ->withConsecutive(
  128. ['USER_AGENT'],
  129. ['OCS-APIREQUEST']
  130. )
  131. ->willReturnMap([
  132. ['USER_AGENT', 'Mac OS X Sync Client'],
  133. ['OCS-APIREQUEST', 'true'],
  134. ]);
  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. 'user' => '',
  175. 'direct' => 0
  176. ],
  177. 'guest'
  178. );
  179. $csp = new Http\ContentSecurityPolicy();
  180. $csp->addAllowedFormActionDomain('nc://*');
  181. $expected->setContentSecurityPolicy($csp);
  182. $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage());
  183. }
  184. public function testShowAuthPickerPageWithOauth() {
  185. $this->request
  186. ->method('getHeader')
  187. ->withConsecutive(
  188. ['USER_AGENT'],
  189. ['OCS-APIREQUEST']
  190. )
  191. ->willReturnMap([
  192. ['USER_AGENT', 'Mac OS X Sync Client'],
  193. ['OCS-APIREQUEST', 'false'],
  194. ]);
  195. $client = new Client();
  196. $client->setName('My external service');
  197. $client->setRedirectUri('https://example.com/redirect.php');
  198. $this->clientMapper
  199. ->expects($this->once())
  200. ->method('getByIdentifier')
  201. ->with('MyClientIdentifier')
  202. ->willReturn($client);
  203. $this->random
  204. ->expects($this->once())
  205. ->method('generate')
  206. ->with(
  207. 64,
  208. ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
  209. )
  210. ->willReturn('StateToken');
  211. $this->session
  212. ->expects($this->once())
  213. ->method('set')
  214. ->with('client.flow.state.token', 'StateToken');
  215. $this->session
  216. ->expects($this->once())
  217. ->method('get')
  218. ->with('oauth.state')
  219. ->willReturn('OauthStateToken');
  220. $this->defaults
  221. ->expects($this->once())
  222. ->method('getName')
  223. ->willReturn('ExampleCloud');
  224. $this->request
  225. ->expects($this->once())
  226. ->method('getServerHost')
  227. ->willReturn('example.com');
  228. $this->request
  229. ->method('getServerProtocol')
  230. ->willReturn('https');
  231. $expected = new StandaloneTemplateResponse(
  232. 'core',
  233. 'loginflow/authpicker',
  234. [
  235. 'client' => 'My external service',
  236. 'clientIdentifier' => 'MyClientIdentifier',
  237. 'instanceName' => 'ExampleCloud',
  238. 'urlGenerator' => $this->urlGenerator,
  239. 'stateToken' => 'StateToken',
  240. 'serverHost' => 'https://example.com',
  241. 'oauthState' => 'OauthStateToken',
  242. 'user' => '',
  243. 'direct' => 0
  244. ],
  245. 'guest'
  246. );
  247. $csp = new Http\ContentSecurityPolicy();
  248. $csp->addAllowedFormActionDomain('https://example.com/redirect.php');
  249. $expected->setContentSecurityPolicy($csp);
  250. $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage('MyClientIdentifier'));
  251. }
  252. public function testGenerateAppPasswordWithInvalidToken() {
  253. $this->session
  254. ->expects($this->once())
  255. ->method('get')
  256. ->with('client.flow.state.token')
  257. ->willReturn('OtherToken');
  258. $this->session
  259. ->expects($this->once())
  260. ->method('remove')
  261. ->with('client.flow.state.token');
  262. $expected = new StandaloneTemplateResponse(
  263. 'core',
  264. '403',
  265. [
  266. 'message' => 'State token does not match',
  267. ],
  268. 'guest'
  269. );
  270. $expected->setStatus(Http::STATUS_FORBIDDEN);
  271. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  272. }
  273. public function testGenerateAppPasswordWithSessionNotAvailableException() {
  274. $this->session
  275. ->expects($this->once())
  276. ->method('get')
  277. ->with('client.flow.state.token')
  278. ->willReturn('MyStateToken');
  279. $this->session
  280. ->expects($this->once())
  281. ->method('remove')
  282. ->with('client.flow.state.token');
  283. $this->session
  284. ->expects($this->once())
  285. ->method('getId')
  286. ->willThrowException(new SessionNotAvailableException());
  287. $expected = new Http\Response();
  288. $expected->setStatus(Http::STATUS_FORBIDDEN);
  289. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  290. }
  291. public function testGenerateAppPasswordWithInvalidTokenException() {
  292. $this->session
  293. ->expects($this->once())
  294. ->method('get')
  295. ->with('client.flow.state.token')
  296. ->willReturn('MyStateToken');
  297. $this->session
  298. ->expects($this->once())
  299. ->method('remove')
  300. ->with('client.flow.state.token');
  301. $this->session
  302. ->expects($this->once())
  303. ->method('getId')
  304. ->willReturn('SessionId');
  305. $this->tokenProvider
  306. ->expects($this->once())
  307. ->method('getToken')
  308. ->with('SessionId')
  309. ->willThrowException(new InvalidTokenException());
  310. $expected = new Http\Response();
  311. $expected->setStatus(Http::STATUS_FORBIDDEN);
  312. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  313. }
  314. public function testGeneratePasswordWithPassword() {
  315. $this->session
  316. ->expects($this->once())
  317. ->method('get')
  318. ->with('client.flow.state.token')
  319. ->willReturn('MyStateToken');
  320. $this->session
  321. ->expects($this->once())
  322. ->method('remove')
  323. ->with('client.flow.state.token');
  324. $this->session
  325. ->expects($this->once())
  326. ->method('getId')
  327. ->willReturn('SessionId');
  328. $myToken = $this->createMock(IToken::class);
  329. $myToken
  330. ->expects($this->once())
  331. ->method('getLoginName')
  332. ->willReturn('MyLoginName');
  333. $this->tokenProvider
  334. ->expects($this->once())
  335. ->method('getToken')
  336. ->with('SessionId')
  337. ->willReturn($myToken);
  338. $this->tokenProvider
  339. ->expects($this->once())
  340. ->method('getPassword')
  341. ->with($myToken, 'SessionId')
  342. ->willReturn('MyPassword');
  343. $this->random
  344. ->expects($this->once())
  345. ->method('generate')
  346. ->with(72)
  347. ->willReturn('MyGeneratedToken');
  348. $user = $this->createMock(IUser::class);
  349. $user
  350. ->expects($this->once())
  351. ->method('getUID')
  352. ->willReturn('MyUid');
  353. $this->userSession
  354. ->expects($this->once())
  355. ->method('getUser')
  356. ->willReturn($user);
  357. $this->tokenProvider
  358. ->expects($this->once())
  359. ->method('generateToken')
  360. ->with(
  361. 'MyGeneratedToken',
  362. 'MyUid',
  363. 'MyLoginName',
  364. 'MyPassword',
  365. 'unknown',
  366. IToken::PERMANENT_TOKEN,
  367. IToken::DO_NOT_REMEMBER
  368. );
  369. $this->request
  370. ->expects($this->once())
  371. ->method('getServerProtocol')
  372. ->willReturn('http');
  373. $this->request
  374. ->expects($this->once())
  375. ->method('getServerHost')
  376. ->willReturn('example.com');
  377. $this->request
  378. ->expects($this->any())
  379. ->method('getHeader')
  380. ->willReturn('');
  381. $this->eventDispatcher->expects($this->once())
  382. ->method('dispatchTyped');
  383. $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
  384. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  385. }
  386. /**
  387. * @param string $redirectUri
  388. * @param string $redirectUrl
  389. *
  390. * @testWith
  391. * ["https://example.com/redirect.php", "https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode"]
  392. * ["https://example.com/redirect.php?hello=world", "https://example.com/redirect.php?hello=world&state=MyOauthState&code=MyAccessCode"]
  393. *
  394. */
  395. public function testGeneratePasswordWithPasswordForOauthClient($redirectUri, $redirectUrl) {
  396. $this->session
  397. ->method('get')
  398. ->withConsecutive(
  399. ['client.flow.state.token'],
  400. ['oauth.state']
  401. )
  402. ->willReturnMap([
  403. ['client.flow.state.token', 'MyStateToken'],
  404. ['oauth.state', 'MyOauthState'],
  405. ]);
  406. $this->session
  407. ->method('remove')
  408. ->withConsecutive(
  409. ['client.flow.state.token'],
  410. ['oauth.state']
  411. );
  412. $this->session
  413. ->expects($this->once())
  414. ->method('getId')
  415. ->willReturn('SessionId');
  416. $myToken = $this->createMock(IToken::class);
  417. $myToken
  418. ->expects($this->once())
  419. ->method('getLoginName')
  420. ->willReturn('MyLoginName');
  421. $this->tokenProvider
  422. ->expects($this->once())
  423. ->method('getToken')
  424. ->with('SessionId')
  425. ->willReturn($myToken);
  426. $this->tokenProvider
  427. ->expects($this->once())
  428. ->method('getPassword')
  429. ->with($myToken, 'SessionId')
  430. ->willReturn('MyPassword');
  431. $this->random
  432. ->method('generate')
  433. ->withConsecutive(
  434. [72],
  435. [128]
  436. )
  437. ->willReturnMap([
  438. [72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS, 'MyGeneratedToken'],
  439. [128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS, 'MyAccessCode'],
  440. ]);
  441. $user = $this->createMock(IUser::class);
  442. $user
  443. ->expects($this->once())
  444. ->method('getUID')
  445. ->willReturn('MyUid');
  446. $this->userSession
  447. ->expects($this->once())
  448. ->method('getUser')
  449. ->willReturn($user);
  450. $token = $this->createMock(IToken::class);
  451. $this->tokenProvider
  452. ->expects($this->once())
  453. ->method('generateToken')
  454. ->with(
  455. 'MyGeneratedToken',
  456. 'MyUid',
  457. 'MyLoginName',
  458. 'MyPassword',
  459. 'My OAuth client',
  460. IToken::PERMANENT_TOKEN,
  461. IToken::DO_NOT_REMEMBER
  462. )
  463. ->willReturn($token);
  464. $client = new Client();
  465. $client->setName('My OAuth client');
  466. $client->setRedirectUri($redirectUri);
  467. $this->clientMapper
  468. ->expects($this->once())
  469. ->method('getByIdentifier')
  470. ->with('MyClientIdentifier')
  471. ->willReturn($client);
  472. $this->eventDispatcher->expects($this->once())
  473. ->method('dispatchTyped');
  474. $expected = new Http\RedirectResponse($redirectUrl);
  475. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier'));
  476. }
  477. public function testGeneratePasswordWithoutPassword() {
  478. $this->session
  479. ->expects($this->once())
  480. ->method('get')
  481. ->with('client.flow.state.token')
  482. ->willReturn('MyStateToken');
  483. $this->session
  484. ->expects($this->once())
  485. ->method('remove')
  486. ->with('client.flow.state.token');
  487. $this->session
  488. ->expects($this->once())
  489. ->method('getId')
  490. ->willReturn('SessionId');
  491. $myToken = $this->createMock(IToken::class);
  492. $myToken
  493. ->expects($this->once())
  494. ->method('getLoginName')
  495. ->willReturn('MyLoginName');
  496. $this->tokenProvider
  497. ->expects($this->once())
  498. ->method('getToken')
  499. ->with('SessionId')
  500. ->willReturn($myToken);
  501. $this->tokenProvider
  502. ->expects($this->once())
  503. ->method('getPassword')
  504. ->with($myToken, 'SessionId')
  505. ->willThrowException(new PasswordlessTokenException());
  506. $this->random
  507. ->expects($this->once())
  508. ->method('generate')
  509. ->with(72)
  510. ->willReturn('MyGeneratedToken');
  511. $user = $this->createMock(IUser::class);
  512. $user
  513. ->expects($this->once())
  514. ->method('getUID')
  515. ->willReturn('MyUid');
  516. $this->userSession
  517. ->expects($this->once())
  518. ->method('getUser')
  519. ->willReturn($user);
  520. $this->tokenProvider
  521. ->expects($this->once())
  522. ->method('generateToken')
  523. ->with(
  524. 'MyGeneratedToken',
  525. 'MyUid',
  526. 'MyLoginName',
  527. null,
  528. 'unknown',
  529. IToken::PERMANENT_TOKEN,
  530. IToken::DO_NOT_REMEMBER
  531. );
  532. $this->request
  533. ->expects($this->once())
  534. ->method('getServerProtocol')
  535. ->willReturn('http');
  536. $this->request
  537. ->expects($this->once())
  538. ->method('getServerHost')
  539. ->willReturn('example.com');
  540. $this->request
  541. ->expects($this->any())
  542. ->method('getHeader')
  543. ->willReturn('');
  544. $this->eventDispatcher->expects($this->once())
  545. ->method('dispatchTyped');
  546. $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
  547. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  548. }
  549. public function dataGeneratePasswordWithHttpsProxy() {
  550. return [
  551. [
  552. [
  553. ['X-Forwarded-Proto', 'http'],
  554. ['X-Forwarded-Ssl', 'off'],
  555. ['USER_AGENT', ''],
  556. ],
  557. 'http',
  558. 'http',
  559. ],
  560. [
  561. [
  562. ['X-Forwarded-Proto', 'http'],
  563. ['X-Forwarded-Ssl', 'off'],
  564. ['USER_AGENT', ''],
  565. ],
  566. 'https',
  567. 'https',
  568. ],
  569. [
  570. [
  571. ['X-Forwarded-Proto', 'https'],
  572. ['X-Forwarded-Ssl', 'off'],
  573. ['USER_AGENT', ''],
  574. ],
  575. 'http',
  576. 'https',
  577. ],
  578. [
  579. [
  580. ['X-Forwarded-Proto', 'https'],
  581. ['X-Forwarded-Ssl', 'on'],
  582. ['USER_AGENT', ''],
  583. ],
  584. 'http',
  585. 'https',
  586. ],
  587. [
  588. [
  589. ['X-Forwarded-Proto', 'http'],
  590. ['X-Forwarded-Ssl', 'on'],
  591. ['USER_AGENT', ''],
  592. ],
  593. 'http',
  594. 'https',
  595. ],
  596. ];
  597. }
  598. /**
  599. * @dataProvider dataGeneratePasswordWithHttpsProxy
  600. * @param array $headers
  601. * @param string $protocol
  602. * @param string $expected
  603. */
  604. public function testGeneratePasswordWithHttpsProxy(array $headers, $protocol, $expected) {
  605. $this->session
  606. ->expects($this->once())
  607. ->method('get')
  608. ->with('client.flow.state.token')
  609. ->willReturn('MyStateToken');
  610. $this->session
  611. ->expects($this->once())
  612. ->method('remove')
  613. ->with('client.flow.state.token');
  614. $this->session
  615. ->expects($this->once())
  616. ->method('getId')
  617. ->willReturn('SessionId');
  618. $myToken = $this->createMock(IToken::class);
  619. $myToken
  620. ->expects($this->once())
  621. ->method('getLoginName')
  622. ->willReturn('MyLoginName');
  623. $this->tokenProvider
  624. ->expects($this->once())
  625. ->method('getToken')
  626. ->with('SessionId')
  627. ->willReturn($myToken);
  628. $this->tokenProvider
  629. ->expects($this->once())
  630. ->method('getPassword')
  631. ->with($myToken, 'SessionId')
  632. ->willReturn('MyPassword');
  633. $this->random
  634. ->expects($this->once())
  635. ->method('generate')
  636. ->with(72)
  637. ->willReturn('MyGeneratedToken');
  638. $user = $this->createMock(IUser::class);
  639. $user
  640. ->expects($this->once())
  641. ->method('getUID')
  642. ->willReturn('MyUid');
  643. $this->userSession
  644. ->expects($this->once())
  645. ->method('getUser')
  646. ->willReturn($user);
  647. $this->tokenProvider
  648. ->expects($this->once())
  649. ->method('generateToken')
  650. ->with(
  651. 'MyGeneratedToken',
  652. 'MyUid',
  653. 'MyLoginName',
  654. 'MyPassword',
  655. 'unknown',
  656. IToken::PERMANENT_TOKEN,
  657. IToken::DO_NOT_REMEMBER
  658. );
  659. $this->request
  660. ->expects($this->once())
  661. ->method('getServerProtocol')
  662. ->willReturn($protocol);
  663. $this->request
  664. ->expects($this->once())
  665. ->method('getServerHost')
  666. ->willReturn('example.com');
  667. $this->request
  668. ->expects($this->atLeastOnce())
  669. ->method('getHeader')
  670. ->willReturnMap($headers);
  671. $this->eventDispatcher->expects($this->once())
  672. ->method('dispatchTyped');
  673. $expected = new Http\RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken');
  674. $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
  675. }
  676. }