PublicKeyTokenProviderTest.php 21 KB

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