PublicKeyTokenProviderTest.php 21 KB

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