PublicKeyTokenProviderTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test\Authentication\Token;
  8. use OC\Authentication\Exceptions\ExpiredTokenException;
  9. use OC\Authentication\Exceptions\InvalidTokenException;
  10. use OC\Authentication\Exceptions\PasswordlessTokenException;
  11. use OC\Authentication\Token\PublicKeyToken;
  12. use OC\Authentication\Token\PublicKeyTokenMapper;
  13. use OC\Authentication\Token\PublicKeyTokenProvider;
  14. use OCP\AppFramework\Db\DoesNotExistException;
  15. use OCP\AppFramework\Utility\ITimeFactory;
  16. use OCP\Authentication\Token\IToken;
  17. use OCP\ICacheFactory;
  18. use OCP\IConfig;
  19. use OCP\IDBConnection;
  20. use OCP\Security\ICrypto;
  21. use OCP\Security\IHasher;
  22. use PHPUnit\Framework\MockObject\MockObject;
  23. use Psr\Log\LoggerInterface;
  24. use Test\TestCase;
  25. class PublicKeyTokenProviderTest extends TestCase {
  26. /** @var PublicKeyTokenProvider|\PHPUnit\Framework\MockObject\MockObject */
  27. private $tokenProvider;
  28. /** @var PublicKeyTokenMapper|\PHPUnit\Framework\MockObject\MockObject */
  29. private $mapper;
  30. /** @var IHasher|\PHPUnit\Framework\MockObject\MockObject */
  31. private $hasher;
  32. /** @var ICrypto */
  33. private $crypto;
  34. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  35. private $config;
  36. /** @var IDBConnection|MockObject */
  37. private IDBConnection $db;
  38. /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
  39. private $logger;
  40. /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
  41. private $timeFactory;
  42. /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
  43. private $cacheFactory;
  44. /** @var int */
  45. private $time;
  46. protected function setUp(): void {
  47. parent::setUp();
  48. $this->mapper = $this->createMock(PublicKeyTokenMapper::class);
  49. $this->hasher = \OC::$server->get(IHasher::class);
  50. $this->crypto = \OC::$server->getCrypto();
  51. $this->config = $this->createMock(IConfig::class);
  52. $this->config->method('getSystemValue')
  53. ->willReturnMap([
  54. ['openssl', [], []],
  55. ]);
  56. $this->config->method('getSystemValueString')
  57. ->willReturnMap([
  58. ['secret', '', '1f4h9s'],
  59. ]);
  60. $this->db = $this->createMock(IDBConnection::class);
  61. $this->logger = $this->createMock(LoggerInterface::class);
  62. $this->timeFactory = $this->createMock(ITimeFactory::class);
  63. $this->time = 1313131;
  64. $this->timeFactory->method('getTime')
  65. ->willReturn($this->time);
  66. $this->cacheFactory = $this->createMock(ICacheFactory::class);
  67. $this->tokenProvider = new PublicKeyTokenProvider(
  68. $this->mapper,
  69. $this->crypto,
  70. $this->config,
  71. $this->db,
  72. $this->logger,
  73. $this->timeFactory,
  74. $this->hasher,
  75. $this->cacheFactory,
  76. );
  77. }
  78. public function testGenerateToken(): void {
  79. $token = 'tokentokentokentokentoken';
  80. $uid = 'user';
  81. $user = 'User';
  82. $password = 'passme';
  83. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  84. $type = IToken::PERMANENT_TOKEN;
  85. $this->config->method('getSystemValueBool')
  86. ->willReturnMap([
  87. ['auth.storeCryptedPassword', true, true],
  88. ]);
  89. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  90. $this->assertInstanceOf(PublicKeyToken::class, $actual);
  91. $this->assertSame($uid, $actual->getUID());
  92. $this->assertSame($user, $actual->getLoginName());
  93. $this->assertSame($name, $actual->getName());
  94. $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
  95. $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
  96. }
  97. public function testGenerateTokenNoPassword(): void {
  98. $token = 'tokentokentokentokentoken';
  99. $uid = 'user';
  100. $user = 'User';
  101. $password = 'passme';
  102. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  103. $type = IToken::PERMANENT_TOKEN;
  104. $this->config->method('getSystemValueBool')
  105. ->willReturnMap([
  106. ['auth.storeCryptedPassword', true, false],
  107. ]);
  108. $this->expectException(PasswordlessTokenException::class);
  109. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  110. $this->assertInstanceOf(PublicKeyToken::class, $actual);
  111. $this->assertSame($uid, $actual->getUID());
  112. $this->assertSame($user, $actual->getLoginName());
  113. $this->assertSame($name, $actual->getName());
  114. $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
  115. $this->tokenProvider->getPassword($actual, $token);
  116. }
  117. public function testGenerateTokenLongPassword(): void {
  118. $token = 'tokentokentokentokentoken';
  119. $uid = 'user';
  120. $user = 'User';
  121. $password = '';
  122. for ($i = 0; $i < 500; $i++) {
  123. $password .= 'e';
  124. }
  125. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  126. $type = IToken::PERMANENT_TOKEN;
  127. $this->config->method('getSystemValueBool')
  128. ->willReturnMap([
  129. ['auth.storeCryptedPassword', true, true],
  130. ]);
  131. $this->expectException(\RuntimeException::class);
  132. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  133. }
  134. public function testGenerateTokenInvalidName(): void {
  135. $token = 'tokentokentokentokentoken';
  136. $uid = 'user';
  137. $user = 'User';
  138. $password = 'passme';
  139. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
  140. . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
  141. . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
  142. . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  143. $type = IToken::PERMANENT_TOKEN;
  144. $this->config->method('getSystemValueBool')
  145. ->willReturnMap([
  146. ['auth.storeCryptedPassword', true, true],
  147. ]);
  148. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  149. $this->assertInstanceOf(PublicKeyToken::class, $actual);
  150. $this->assertSame($uid, $actual->getUID());
  151. $this->assertSame($user, $actual->getLoginName());
  152. $this->assertSame('User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12User-Agent: Mozill…', $actual->getName());
  153. $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
  154. $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
  155. }
  156. public function testUpdateToken(): void {
  157. $tk = new PublicKeyToken();
  158. $this->mapper->expects($this->once())
  159. ->method('updateActivity')
  160. ->with($tk, $this->time);
  161. $tk->setLastActivity($this->time - 200);
  162. $this->config->method('getSystemValueBool')
  163. ->willReturnMap([
  164. ['auth.storeCryptedPassword', true, true],
  165. ]);
  166. $this->tokenProvider->updateTokenActivity($tk);
  167. $this->assertEquals($this->time, $tk->getLastActivity());
  168. }
  169. public function testUpdateTokenDebounce(): void {
  170. $tk = new PublicKeyToken();
  171. $this->config->method('getSystemValueInt')
  172. ->willReturnCallback(function ($value, $default) {
  173. return $default;
  174. });
  175. $tk->setLastActivity($this->time - 30);
  176. $this->mapper->expects($this->never())
  177. ->method('updateActivity')
  178. ->with($tk, $this->time);
  179. $this->tokenProvider->updateTokenActivity($tk);
  180. }
  181. public function testGetTokenByUser(): void {
  182. $this->mapper->expects($this->once())
  183. ->method('getTokenByUser')
  184. ->with('uid')
  185. ->willReturn(['token']);
  186. $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid'));
  187. }
  188. public function testGetPassword(): void {
  189. $token = 'tokentokentokentokentoken';
  190. $uid = 'user';
  191. $user = 'User';
  192. $password = 'passme';
  193. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  194. $type = IToken::PERMANENT_TOKEN;
  195. $this->config->method('getSystemValueBool')
  196. ->willReturnMap([
  197. ['auth.storeCryptedPassword', true, true],
  198. ]);
  199. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  200. $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
  201. }
  202. public function testGetPasswordPasswordLessToken(): void {
  203. $this->expectException(\OC\Authentication\Exceptions\PasswordlessTokenException::class);
  204. $token = 'token1234';
  205. $tk = new PublicKeyToken();
  206. $tk->setPassword(null);
  207. $this->tokenProvider->getPassword($tk, $token);
  208. }
  209. public function testGetPasswordInvalidToken(): void {
  210. $this->expectException(\OC\Authentication\Exceptions\InvalidTokenException::class);
  211. $token = 'tokentokentokentokentoken';
  212. $uid = 'user';
  213. $user = 'User';
  214. $password = 'passme';
  215. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  216. $type = IToken::PERMANENT_TOKEN;
  217. $this->config->method('getSystemValueBool')
  218. ->willReturnMap([
  219. ['auth.storeCryptedPassword', true, true],
  220. ]);
  221. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  222. $this->tokenProvider->getPassword($actual, 'wrongtoken');
  223. }
  224. public function testSetPassword(): void {
  225. $token = 'tokentokentokentokentoken';
  226. $uid = 'user';
  227. $user = 'User';
  228. $password = 'passme';
  229. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  230. $type = IToken::PERMANENT_TOKEN;
  231. $this->config->method('getSystemValueBool')
  232. ->willReturnMap([
  233. ['auth.storeCryptedPassword', true, true],
  234. ]);
  235. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  236. $this->mapper->method('getTokenByUser')
  237. ->with('user')
  238. ->willReturn([$actual]);
  239. $newpass = 'newpass';
  240. $this->mapper->expects($this->once())
  241. ->method('update')
  242. ->with($this->callback(function ($token) use ($newpass) {
  243. return $newpass === $this->tokenProvider->getPassword($token, 'tokentokentokentokentoken');
  244. }));
  245. $this->tokenProvider->setPassword($actual, $token, $newpass);
  246. $this->assertSame($newpass, $this->tokenProvider->getPassword($actual, 'tokentokentokentokentoken'));
  247. }
  248. public function testSetPasswordInvalidToken(): void {
  249. $this->expectException(\OC\Authentication\Exceptions\InvalidTokenException::class);
  250. $token = $this->createMock(IToken::class);
  251. $tokenId = 'token123';
  252. $password = '123456';
  253. $this->tokenProvider->setPassword($token, $tokenId, $password);
  254. }
  255. public function testInvalidateToken(): void {
  256. $this->mapper->expects($this->exactly(2))
  257. ->method('invalidate')
  258. ->withConsecutive(
  259. [hash('sha512', 'token7'.'1f4h9s')],
  260. [hash('sha512', 'token7')]
  261. );
  262. $this->tokenProvider->invalidateToken('token7');
  263. }
  264. public function testInvalidateTokenById(): void {
  265. $id = 123;
  266. $this->mapper->expects($this->once())
  267. ->method('getTokenById')
  268. ->with($id);
  269. $this->tokenProvider->invalidateTokenById('uid', $id);
  270. }
  271. public function testInvalidateOldTokens(): void {
  272. $defaultSessionLifetime = 60 * 60 * 24;
  273. $defaultRememberMeLifetime = 60 * 60 * 24 * 15;
  274. $wipeTokenLifetime = 60 * 60 * 24 * 60;
  275. $this->config->expects($this->exactly(4))
  276. ->method('getSystemValueInt')
  277. ->willReturnMap([
  278. ['session_lifetime', $defaultSessionLifetime, 150],
  279. ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300],
  280. ['token_auth_wipe_token_retention', $wipeTokenLifetime, 500],
  281. ['token_auth_token_retention', 60 * 60 * 24 * 365, 800],
  282. ]);
  283. $this->mapper->expects($this->exactly(4))
  284. ->method('invalidateOld')
  285. ->withConsecutive(
  286. [$this->time - 150, IToken::TEMPORARY_TOKEN, IToken::DO_NOT_REMEMBER],
  287. [$this->time - 300, IToken::TEMPORARY_TOKEN, IToken::REMEMBER],
  288. [$this->time - 500, IToken::WIPE_TOKEN, null],
  289. [$this->time - 800, IToken::PERMANENT_TOKEN, null],
  290. );
  291. $this->tokenProvider->invalidateOldTokens();
  292. }
  293. public function testInvalidateLastUsedBefore(): void {
  294. $this->mapper->expects($this->once())
  295. ->method('invalidateLastUsedBefore')
  296. ->with('user', 946684800);
  297. $this->tokenProvider->invalidateLastUsedBefore('user', 946684800);
  298. }
  299. public function testRenewSessionTokenWithoutPassword(): void {
  300. $token = 'oldIdtokentokentokentoken';
  301. $uid = 'user';
  302. $user = 'User';
  303. $password = null;
  304. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  305. $type = IToken::PERMANENT_TOKEN;
  306. $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  307. $this->mapper
  308. ->expects($this->once())
  309. ->method('getToken')
  310. ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
  311. ->willReturn($oldToken);
  312. $this->mapper
  313. ->expects($this->once())
  314. ->method('insert')
  315. ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name) {
  316. return $token->getUID() === $uid &&
  317. $token->getLoginName() === $user &&
  318. $token->getName() === $name &&
  319. $token->getType() === IToken::DO_NOT_REMEMBER &&
  320. $token->getLastActivity() === $this->time &&
  321. $token->getPassword() === null;
  322. }));
  323. $this->mapper
  324. ->expects($this->once())
  325. ->method('delete')
  326. ->with($this->callback(function ($token) use ($oldToken) {
  327. return $token === $oldToken;
  328. }));
  329. $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
  330. }
  331. public function testRenewSessionTokenWithPassword(): void {
  332. $token = 'oldIdtokentokentokentoken';
  333. $uid = 'user';
  334. $user = 'User';
  335. $password = 'password';
  336. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  337. $type = IToken::PERMANENT_TOKEN;
  338. $this->config->method('getSystemValueBool')
  339. ->willReturnMap([
  340. ['auth.storeCryptedPassword', true, true],
  341. ]);
  342. $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  343. $this->mapper
  344. ->expects($this->once())
  345. ->method('getToken')
  346. ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
  347. ->willReturn($oldToken);
  348. $this->mapper
  349. ->expects($this->once())
  350. ->method('insert')
  351. ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name): bool {
  352. return $token->getUID() === $uid &&
  353. $token->getLoginName() === $user &&
  354. $token->getName() === $name &&
  355. $token->getType() === IToken::DO_NOT_REMEMBER &&
  356. $token->getLastActivity() === $this->time &&
  357. $token->getPassword() !== null &&
  358. $this->tokenProvider->getPassword($token, 'newIdtokentokentokentoken') === 'password';
  359. }));
  360. $this->mapper
  361. ->expects($this->once())
  362. ->method('delete')
  363. ->with($this->callback(function ($token) use ($oldToken): bool {
  364. return $token === $oldToken;
  365. }));
  366. $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
  367. }
  368. public function testGetToken(): void {
  369. $token = new PublicKeyToken();
  370. $this->config->method('getSystemValue')
  371. ->with('secret')
  372. ->willReturn('mysecret');
  373. $this->mapper->method('getToken')
  374. ->with(
  375. $this->callback(function (string $token) {
  376. return hash('sha512', 'unhashedTokentokentokentokentoken'.'1f4h9s') === $token;
  377. })
  378. )->willReturn($token);
  379. $this->assertSame($token, $this->tokenProvider->getToken('unhashedTokentokentokentokentoken'));
  380. }
  381. public function testGetInvalidToken(): void {
  382. $this->expectException(InvalidTokenException::class);
  383. $this->mapper->expects($this->exactly(2))
  384. ->method('getToken')
  385. ->withConsecutive(
  386. [$this->callback(function (string $token): bool {
  387. return hash('sha512', 'unhashedTokentokentokentokentoken'.'1f4h9s') === $token;
  388. })],
  389. [$this->callback(function (string $token): bool {
  390. return hash('sha512', 'unhashedTokentokentokentokentoken') === $token;
  391. })]
  392. )->willThrowException(new DoesNotExistException('nope'));
  393. $this->tokenProvider->getToken('unhashedTokentokentokentokentoken');
  394. }
  395. public function testGetExpiredToken(): void {
  396. $token = 'tokentokentokentokentoken';
  397. $uid = 'user';
  398. $user = 'User';
  399. $password = 'passme';
  400. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  401. $type = IToken::PERMANENT_TOKEN;
  402. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  403. $actual->setExpires(42);
  404. $this->mapper->method('getToken')
  405. ->with(
  406. $this->callback(function (string $token) {
  407. return hash('sha512', 'tokentokentokentokentoken'.'1f4h9s') === $token;
  408. })
  409. )->willReturn($actual);
  410. try {
  411. $this->tokenProvider->getToken('tokentokentokentokentoken');
  412. $this->fail();
  413. } catch (ExpiredTokenException $e) {
  414. $this->assertSame($actual, $e->getToken());
  415. }
  416. }
  417. public function testGetTokenById(): void {
  418. $token = $this->createMock(PublicKeyToken::class);
  419. $this->mapper->expects($this->once())
  420. ->method('getTokenById')
  421. ->with($this->equalTo(42))
  422. ->willReturn($token);
  423. $this->assertSame($token, $this->tokenProvider->getTokenById(42));
  424. }
  425. public function testGetInvalidTokenById(): void {
  426. $this->expectException(InvalidTokenException::class);
  427. $this->mapper->expects($this->once())
  428. ->method('getTokenById')
  429. ->with($this->equalTo(42))
  430. ->willThrowException(new DoesNotExistException('nope'));
  431. $this->tokenProvider->getTokenById(42);
  432. }
  433. public function testGetExpiredTokenById(): void {
  434. $token = new PublicKeyToken();
  435. $token->setExpires(42);
  436. $this->mapper->expects($this->once())
  437. ->method('getTokenById')
  438. ->with($this->equalTo(42))
  439. ->willReturn($token);
  440. try {
  441. $this->tokenProvider->getTokenById(42);
  442. $this->fail();
  443. } catch (ExpiredTokenException $e) {
  444. $this->assertSame($token, $e->getToken());
  445. }
  446. }
  447. public function testRotate(): void {
  448. $token = 'oldtokentokentokentokentoken';
  449. $uid = 'user';
  450. $user = 'User';
  451. $password = 'password';
  452. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  453. $type = IToken::PERMANENT_TOKEN;
  454. $this->config->method('getSystemValueBool')
  455. ->willReturnMap([
  456. ['auth.storeCryptedPassword', true, true],
  457. ]);
  458. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  459. $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
  460. $this->assertSame('password', $this->tokenProvider->getPassword($new, 'newtokentokentokentokentoken'));
  461. }
  462. public function testRotateNoPassword(): void {
  463. $token = 'oldtokentokentokentokentoken';
  464. $uid = 'user';
  465. $user = 'User';
  466. $password = null;
  467. $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
  468. $type = IToken::PERMANENT_TOKEN;
  469. $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
  470. $oldPrivate = $actual->getPrivateKey();
  471. $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
  472. $newPrivate = $new->getPrivateKey();
  473. $this->assertNotSame($newPrivate, $oldPrivate);
  474. $this->assertNull($new->getPassword());
  475. }
  476. public function testMarkPasswordInvalidInvalidToken(): void {
  477. $token = $this->createMock(IToken::class);
  478. $this->expectException(InvalidTokenException::class);
  479. $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
  480. }
  481. public function testMarkPasswordInvalid(): void {
  482. $token = $this->createMock(PublicKeyToken::class);
  483. $token->expects($this->once())
  484. ->method('setPasswordInvalid')
  485. ->with(true);
  486. $this->mapper->expects($this->once())
  487. ->method('update')
  488. ->with($token);
  489. $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
  490. }
  491. public function testUpdatePasswords(): void {
  492. $uid = 'myUID';
  493. $token1 = $this->tokenProvider->generateToken(
  494. 'foobetokentokentokentoken',
  495. $uid,
  496. $uid,
  497. 'bar',
  498. 'random1',
  499. IToken::PERMANENT_TOKEN,
  500. IToken::REMEMBER);
  501. $token2 = $this->tokenProvider->generateToken(
  502. 'foobartokentokentokentoken',
  503. $uid,
  504. $uid,
  505. 'bar',
  506. 'random2',
  507. IToken::PERMANENT_TOKEN,
  508. IToken::REMEMBER);
  509. $this->config->method('getSystemValueBool')
  510. ->willReturnMap([
  511. ['auth.storeCryptedPassword', true, true],
  512. ]);
  513. $this->mapper->method('hasExpiredTokens')
  514. ->with($uid)
  515. ->willReturn(true);
  516. $this->mapper->expects($this->once())
  517. ->method('getTokenByUser')
  518. ->with($uid)
  519. ->willReturn([$token1, $token2]);
  520. $this->mapper->expects($this->exactly(2))
  521. ->method('update')
  522. ->with($this->callback(function (PublicKeyToken $t) use ($token1, $token2) {
  523. return $t === $token1 || $t === $token2;
  524. }));
  525. $this->tokenProvider->updatePasswords($uid, 'bar2');
  526. }
  527. }