aboutsummaryrefslogtreecommitdiffstats
path: root/tests/lib/Authentication/Token
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/Authentication/Token')
-rw-r--r--tests/lib/Authentication/Token/ManagerTest.php406
-rw-r--r--tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php265
-rw-r--r--tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php645
-rw-r--r--tests/lib/Authentication/Token/PublicKeyTokenTest.php29
-rw-r--r--tests/lib/Authentication/Token/RemoteWipeTest.php173
5 files changed, 1518 insertions, 0 deletions
diff --git a/tests/lib/Authentication/Token/ManagerTest.php b/tests/lib/Authentication/Token/ManagerTest.php
new file mode 100644
index 00000000000..58bbe236248
--- /dev/null
+++ b/tests/lib/Authentication/Token/ManagerTest.php
@@ -0,0 +1,406 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Authentication\Token;
+
+use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
+use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Token\IToken;
+use OC\Authentication\Token\Manager;
+use OC\Authentication\Token\PublicKeyToken;
+use OC\Authentication\Token\PublicKeyTokenProvider;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class ManagerTest extends TestCase {
+ /** @var PublicKeyTokenProvider|MockObject */
+ private $publicKeyTokenProvider;
+ /** @var Manager */
+ private $manager;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->publicKeyTokenProvider = $this->createMock(PublicKeyTokenProvider::class);
+ $this->manager = new Manager(
+ $this->publicKeyTokenProvider
+ );
+ }
+
+ public function testGenerateToken(): void {
+ $token = new PublicKeyToken();
+
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('generateToken')
+ ->with(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ 'name',
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ )->willReturn($token);
+
+ $actual = $this->manager->generateToken(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ 'name',
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ );
+
+ $this->assertSame($token, $actual);
+ }
+
+ public function testGenerateConflictingToken(): void {
+ /** @var MockObject|UniqueConstraintViolationException $exception */
+ $exception = $this->createMock(UniqueConstraintViolationException::class);
+
+ $token = new PublicKeyToken();
+ $token->setUid('uid');
+
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('generateToken')
+ ->with(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ 'name',
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ )->willThrowException($exception);
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('getToken')
+ ->with('token')
+ ->willReturn($token);
+
+ $actual = $this->manager->generateToken(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ 'name',
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ );
+
+ $this->assertSame($token, $actual);
+ }
+
+ public function testGenerateTokenTooLongName(): void {
+ $token = $this->createMock(IToken::class);
+ $token->method('getName')
+ ->willReturn(str_repeat('a', 120) . '…');
+
+
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('generateToken')
+ ->with(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ str_repeat('a', 120) . '…',
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ )->willReturn($token);
+
+ $actual = $this->manager->generateToken(
+ 'token',
+ 'uid',
+ 'loginName',
+ 'password',
+ str_repeat('a', 200),
+ IToken::TEMPORARY_TOKEN,
+ IToken::REMEMBER
+ );
+
+ $this->assertSame(121, mb_strlen($actual->getName()));
+ }
+
+ public static function tokenData(): array {
+ return [
+ [new PublicKeyToken()],
+ [IToken::class],
+ ];
+ }
+
+ protected function setNoCall(IToken $token) {
+ if (!($token instanceof PublicKeyToken)) {
+ $this->publicKeyTokenProvider->expects($this->never())
+ ->method($this->anything());
+ }
+ }
+
+ protected function setCall(IToken $token, string $function, $return = null) {
+ if ($token instanceof PublicKeyToken) {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method($function)
+ ->with($token)
+ ->willReturn($return);
+ }
+ }
+
+ protected function setException(IToken $token) {
+ if (!($token instanceof PublicKeyToken)) {
+ $this->expectException(InvalidTokenException::class);
+ }
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
+ public function testUpdateToken(IToken|string $token): void {
+ if (is_string($token)) {
+ $token = $this->createMock($token);
+ }
+
+ $this->setNoCall($token);
+ $this->setCall($token, 'updateToken');
+ $this->setException($token);
+
+ $this->manager->updateToken($token);
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
+ public function testUpdateTokenActivity(IToken|string $token): void {
+ if (is_string($token)) {
+ $token = $this->createMock($token);
+ }
+
+ $this->setNoCall($token);
+ $this->setCall($token, 'updateTokenActivity');
+ $this->setException($token);
+
+ $this->manager->updateTokenActivity($token);
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
+ public function testGetPassword(IToken|string $token): void {
+ if (is_string($token)) {
+ $token = $this->createMock($token);
+ }
+
+ $this->setNoCall($token);
+ $this->setCall($token, 'getPassword', 'password');
+ $this->setException($token);
+
+ $result = $this->manager->getPassword($token, 'tokenId', 'password');
+
+ $this->assertSame('password', $result);
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
+ public function testSetPassword(IToken|string $token): void {
+ if (is_string($token)) {
+ $token = $this->createMock($token);
+ }
+
+ $this->setNoCall($token);
+ $this->setCall($token, 'setPassword');
+ $this->setException($token);
+
+ $this->manager->setPassword($token, 'tokenId', 'password');
+ }
+
+ public function testInvalidateTokens(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('invalidateToken')
+ ->with('token');
+
+ $this->manager->invalidateToken('token');
+ }
+
+ public function testInvalidateTokenById(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('invalidateTokenById')
+ ->with('uid', 42);
+
+ $this->manager->invalidateTokenById('uid', 42);
+ }
+
+ public function testInvalidateOldTokens(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('invalidateOldTokens');
+
+ $this->manager->invalidateOldTokens();
+ }
+
+ public function testInvalidateLastUsedBefore(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('invalidateLastUsedBefore')
+ ->with('user', 946684800);
+
+ $this->manager->invalidateLastUsedBefore('user', 946684800);
+ }
+
+ public function testGetTokenByUser(): void {
+ $t1 = new PublicKeyToken();
+ $t2 = new PublicKeyToken();
+
+ $this->publicKeyTokenProvider
+ ->method('getTokenByUser')
+ ->willReturn([$t1, $t2]);
+
+ $result = $this->manager->getTokenByUser('uid');
+
+ $this->assertEquals([$t1, $t2], $result);
+ }
+
+ public function testRenewSessionTokenPublicKey(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('renewSessionToken')
+ ->with('oldId', 'newId');
+
+ $this->manager->renewSessionToken('oldId', 'newId');
+ }
+
+ public function testRenewSessionInvalid(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('renewSessionToken')
+ ->with('oldId', 'newId')
+ ->willThrowException(new InvalidTokenException());
+
+ $this->expectException(InvalidTokenException::class);
+ $this->manager->renewSessionToken('oldId', 'newId');
+ }
+
+ public function testGetTokenByIdPublicKey(): void {
+ $token = $this->createMock(IToken::class);
+
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('getTokenById')
+ ->with(42)
+ ->willReturn($token);
+
+ $this->assertSame($token, $this->manager->getTokenById(42));
+ }
+
+ public function testGetTokenByIdInvalid(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('getTokenById')
+ ->with(42)
+ ->willThrowException(new InvalidTokenException());
+
+ $this->expectException(InvalidTokenException::class);
+ $this->manager->getTokenById(42);
+ }
+
+ public function testGetTokenPublicKey(): void {
+ $token = new PublicKeyToken();
+
+ $this->publicKeyTokenProvider
+ ->method('getToken')
+ ->with('tokenId')
+ ->willReturn($token);
+
+ $this->assertSame($token, $this->manager->getToken('tokenId'));
+ }
+
+ public function testGetTokenInvalid(): void {
+ $this->publicKeyTokenProvider
+ ->method('getToken')
+ ->with('tokenId')
+ ->willThrowException(new InvalidTokenException());
+
+ $this->expectException(InvalidTokenException::class);
+ $this->manager->getToken('tokenId');
+ }
+
+ public function testRotateInvalid(): void {
+ $this->expectException(InvalidTokenException::class);
+ $this->manager->rotate($this->createMock(IToken::class), 'oldId', 'newId');
+ }
+
+ public function testRotatePublicKey(): void {
+ $token = new PublicKeyToken();
+
+ $this->publicKeyTokenProvider
+ ->method('rotate')
+ ->with($token, 'oldId', 'newId')
+ ->willReturn($token);
+
+ $this->assertSame($token, $this->manager->rotate($token, 'oldId', 'newId'));
+ }
+
+ public function testMarkPasswordInvalidPublicKey(): void {
+ $token = $this->createMock(PublicKeyToken::class);
+
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('markPasswordInvalid')
+ ->with($token, 'tokenId');
+
+ $this->manager->markPasswordInvalid($token, 'tokenId');
+ }
+
+ public function testMarkPasswordInvalidInvalidToken(): void {
+ $this->expectException(InvalidTokenException::class);
+
+ $this->manager->markPasswordInvalid($this->createMock(IToken::class), 'tokenId');
+ }
+
+ public function testUpdatePasswords(): void {
+ $this->publicKeyTokenProvider->expects($this->once())
+ ->method('updatePasswords')
+ ->with('uid', 'pass');
+
+ $this->manager->updatePasswords('uid', 'pass');
+ }
+
+ public function testInvalidateTokensOfUserNoClientName(): void {
+ $t1 = new PublicKeyToken();
+ $t2 = new PublicKeyToken();
+ $t1->setId(123);
+ $t2->setId(456);
+
+ $this->publicKeyTokenProvider
+ ->expects($this->once())
+ ->method('getTokenByUser')
+ ->with('theUser')
+ ->willReturn([$t1, $t2]);
+
+ $calls = [
+ ['theUser', 123],
+ ['theUser', 456],
+ ];
+ $this->publicKeyTokenProvider
+ ->expects($this->exactly(2))
+ ->method('invalidateTokenById')
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
+ $this->manager->invalidateTokensOfUser('theUser', null);
+ }
+
+ public function testInvalidateTokensOfUserClientNameGiven(): void {
+ $t1 = new PublicKeyToken();
+ $t2 = new PublicKeyToken();
+ $t3 = new PublicKeyToken();
+ $t1->setId(123);
+ $t1->setName('Firefox session');
+ $t2->setId(456);
+ $t2->setName('My Client Name');
+ $t3->setId(789);
+ $t3->setName('mobile client');
+
+ $this->publicKeyTokenProvider
+ ->expects($this->once())
+ ->method('getTokenByUser')
+ ->with('theUser')
+ ->willReturn([$t1, $t2, $t3]);
+ $this->publicKeyTokenProvider
+ ->expects($this->once())
+ ->method('invalidateTokenById')
+ ->with('theUser', 456);
+ $this->manager->invalidateTokensOfUser('theUser', 'My Client Name');
+ }
+}
diff --git a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php
new file mode 100644
index 00000000000..d1585dadc26
--- /dev/null
+++ b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php
@@ -0,0 +1,265 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Authentication\Token;
+
+use OC\Authentication\Token\PublicKeyToken;
+use OC\Authentication\Token\PublicKeyTokenMapper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\Authentication\Token\IToken;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\IUser;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class PublicKeyTokenMapperTest extends TestCase {
+ /** @var PublicKeyTokenMapper */
+ private $mapper;
+
+ /** @var IDBConnection */
+ private $dbConnection;
+
+ /** @var int */
+ private $time;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->dbConnection = Server::get(IDBConnection::class);
+ $this->time = time();
+ $this->resetDatabase();
+
+ $this->mapper = new PublicKeyTokenMapper($this->dbConnection);
+ }
+
+ private function resetDatabase() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete('authtoken')->execute();
+ $qb->insert('authtoken')->values([
+ 'uid' => $qb->createNamedParameter('user1'),
+ 'login_name' => $qb->createNamedParameter('User1'),
+ 'password' => $qb->createNamedParameter('a75c7116460c082912d8f6860a850904|3nz5qbG1nNSLLi6V|c55365a0e54cfdfac4a175bcf11a7612aea74492277bba6e5d96a24497fa9272488787cb2f3ad34d8b9b8060934fce02f008d371df3ff3848f4aa61944851ff0'),
+ 'name' => $qb->createNamedParameter('Firefox on Linux'),
+ 'token' => $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'),
+ 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
+ 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
+ 'last_check' => $this->time - 60 * 10, // 10mins ago
+ 'public_key' => $qb->createNamedParameter('public key'),
+ 'private_key' => $qb->createNamedParameter('private key'),
+ 'version' => $qb->createNamedParameter(2),
+ ])->execute();
+ $qb->insert('authtoken')->values([
+ 'uid' => $qb->createNamedParameter('user2'),
+ 'login_name' => $qb->createNamedParameter('User2'),
+ 'password' => $qb->createNamedParameter('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f'),
+ 'name' => $qb->createNamedParameter('Firefox on Android'),
+ 'token' => $qb->createNamedParameter('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b'),
+ 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
+ 'last_activity' => $qb->createNamedParameter($this->time - 60 * 60 * 24 * 3, IQueryBuilder::PARAM_INT), // Three days ago
+ 'last_check' => $this->time - 10, // 10secs ago
+ 'public_key' => $qb->createNamedParameter('public key'),
+ 'private_key' => $qb->createNamedParameter('private key'),
+ 'version' => $qb->createNamedParameter(2),
+ ])->execute();
+ $qb->insert('authtoken')->values([
+ 'uid' => $qb->createNamedParameter('user1'),
+ 'login_name' => $qb->createNamedParameter('User1'),
+ 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
+ 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
+ 'token' => $qb->createNamedParameter('47af8697ba590fb82579b5f1b3b6e8066773a62100abbe0db09a289a62f5d980dc300fa3d98b01d7228468d1ab05c1aa14c8d14bd5b6eee9cdf1ac14864680c3'),
+ 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
+ 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
+ 'last_check' => $this->time - 60 * 10, // 10mins ago
+ 'public_key' => $qb->createNamedParameter('public key'),
+ 'private_key' => $qb->createNamedParameter('private key'),
+ 'version' => $qb->createNamedParameter(2),
+ ])->execute();
+ $qb->insert('authtoken')->values([
+ 'uid' => $qb->createNamedParameter('user3'),
+ 'login_name' => $qb->createNamedParameter('User3'),
+ 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
+ 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
+ 'token' => $qb->createNamedParameter('6d9a290d239d09f2cc33a03cc54cccd46f7dc71630dcc27d39214824bd3e093f1feb4e2b55eb159d204caa15dee9556c202a5aa0b9d67806c3f4ec2cde11af67'),
+ 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
+ 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
+ 'last_check' => $this->time - 60 * 10, // 10mins ago
+ 'public_key' => $qb->createNamedParameter('public key'),
+ 'private_key' => $qb->createNamedParameter('private key'),
+ 'version' => $qb->createNamedParameter(2),
+ 'password_invalid' => $qb->createNamedParameter(1),
+ ])->execute();
+ $qb->insert('authtoken')->values([
+ 'uid' => $qb->createNamedParameter('user3'),
+ 'login_name' => $qb->createNamedParameter('User3'),
+ 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
+ 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
+ 'token' => $qb->createNamedParameter('84c5808c6445b6d65b8aa5b03840f09b27de603f0fb970906fb14ea4b115b7bf5ec53fada5c093fe46afdcd7bbc9617253a4d105f7dfb32719f9973d72412f31'),
+ 'type' => $qb->createNamedParameter(IToken::PERMANENT_TOKEN),
+ 'last_activity' => $qb->createNamedParameter($this->time - 60 * 3, IQueryBuilder::PARAM_INT), // Three minutes ago
+ 'last_check' => $this->time - 60 * 10, // 10mins ago
+ 'public_key' => $qb->createNamedParameter('public key'),
+ 'private_key' => $qb->createNamedParameter('private key'),
+ 'version' => $qb->createNamedParameter(2),
+ 'password_invalid' => $qb->createNamedParameter(1),
+ ])->execute();
+ }
+
+ private function getNumberOfTokens() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $result = $qb->select($qb->func()->count('*', 'count'))
+ ->from('authtoken')
+ ->execute()
+ ->fetch();
+ return (int)$result['count'];
+ }
+
+ public function testInvalidate(): void {
+ $token = '9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206';
+
+ $this->mapper->invalidate($token);
+
+ $this->assertSame(4, $this->getNumberOfTokens());
+ }
+
+ public function testInvalidateInvalid(): void {
+ $token = 'youwontfindthisoneinthedatabase';
+
+ $this->mapper->invalidate($token);
+
+ $this->assertSame(5, $this->getNumberOfTokens());
+ }
+
+ public function testInvalidateOld(): void {
+ $olderThan = $this->time - 60 * 60; // One hour
+
+ $this->mapper->invalidateOld($olderThan);
+
+ $this->assertSame(4, $this->getNumberOfTokens());
+ }
+
+ public function testInvalidateLastUsedBefore(): void {
+ $before = $this->time - 60 * 2; // Two minutes
+
+ $this->mapper->invalidateLastUsedBefore('user3', $before);
+
+ $this->assertSame(4, $this->getNumberOfTokens());
+ }
+
+ public function testGetToken(): void {
+ $token = new PublicKeyToken();
+ $token->setUid('user2');
+ $token->setLoginName('User2');
+ $token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
+ $token->setName('Firefox on Android');
+ $token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
+ $token->setType(IToken::TEMPORARY_TOKEN);
+ $token->setRemember(IToken::DO_NOT_REMEMBER);
+ $token->setLastActivity($this->time - 60 * 60 * 24 * 3);
+ $token->setLastCheck($this->time - 10);
+ $token->setPublicKey('public key');
+ $token->setPrivateKey('private key');
+ $token->setVersion(PublicKeyToken::VERSION);
+
+ $dbToken = $this->mapper->getToken($token->getToken());
+
+ $token->setId($dbToken->getId()); // We don't know the ID
+ $token->resetUpdatedFields();
+
+ $this->assertEquals($token, $dbToken);
+ }
+
+
+ public function testGetInvalidToken(): void {
+ $this->expectException(DoesNotExistException::class);
+
+ $token = 'thisisaninvalidtokenthatisnotinthedatabase';
+
+ $this->mapper->getToken($token);
+ }
+
+ public function testGetTokenById(): void {
+ $token = new PublicKeyToken();
+ $token->setUid('user2');
+ $token->setLoginName('User2');
+ $token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
+ $token->setName('Firefox on Android');
+ $token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
+ $token->setType(IToken::TEMPORARY_TOKEN);
+ $token->setRemember(IToken::DO_NOT_REMEMBER);
+ $token->setLastActivity($this->time - 60 * 60 * 24 * 3);
+ $token->setLastCheck($this->time - 10);
+ $token->setPublicKey('public key');
+ $token->setPrivateKey('private key');
+ $token->setVersion(PublicKeyToken::VERSION);
+
+ $dbToken = $this->mapper->getToken($token->getToken());
+ $token->setId($dbToken->getId()); // We don't know the ID
+ $token->resetUpdatedFields();
+
+ $dbToken = $this->mapper->getTokenById($token->getId());
+ $this->assertEquals($token, $dbToken);
+ }
+
+
+ public function testGetTokenByIdNotFound(): void {
+ $this->expectException(DoesNotExistException::class);
+
+ $this->mapper->getTokenById(-1);
+ }
+
+
+ public function testGetInvalidTokenById(): void {
+ $this->expectException(DoesNotExistException::class);
+
+ $id = '42';
+
+ $this->mapper->getToken($id);
+ }
+
+ public function testGetTokenByUser(): void {
+ $this->assertCount(2, $this->mapper->getTokenByUser('user1'));
+ }
+
+ public function testGetTokenByUserNotFound(): void {
+ $this->assertCount(0, $this->mapper->getTokenByUser('user1000'));
+ }
+
+ public function testGetById(): void {
+ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->select('id')
+ ->from('authtoken')
+ ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206')));
+ $result = $qb->execute();
+ $id = $result->fetch()['id'];
+
+ $token = $this->mapper->getTokenById((int)$id);
+ $this->assertEquals('user1', $token->getUID());
+ }
+
+ public function testDeleteByName(): void {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->select('name')
+ ->from('authtoken')
+ ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206')));
+ $result = $qb->execute();
+ $name = $result->fetch()['name'];
+ $this->mapper->deleteByName($name);
+ $this->assertEquals(4, $this->getNumberOfTokens());
+ }
+
+ public function testHasExpiredTokens(): void {
+ $this->assertFalse($this->mapper->hasExpiredTokens('user1'));
+ $this->assertTrue($this->mapper->hasExpiredTokens('user3'));
+ }
+}
diff --git a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php
new file mode 100644
index 00000000000..7e7f949965f
--- /dev/null
+++ b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php
@@ -0,0 +1,645 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Authentication\Token;
+
+use OC\Authentication\Exceptions\ExpiredTokenException;
+use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Exceptions\PasswordlessTokenException;
+use OC\Authentication\Token\PublicKeyToken;
+use OC\Authentication\Token\PublicKeyTokenMapper;
+use OC\Authentication\Token\PublicKeyTokenProvider;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Authentication\Token\IToken;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Security\ICrypto;
+use OCP\Security\IHasher;
+use OCP\Server;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class PublicKeyTokenProviderTest extends TestCase {
+ /** @var PublicKeyTokenProvider|\PHPUnit\Framework\MockObject\MockObject */
+ private $tokenProvider;
+ /** @var PublicKeyTokenMapper|\PHPUnit\Framework\MockObject\MockObject */
+ private $mapper;
+ /** @var IHasher|\PHPUnit\Framework\MockObject\MockObject */
+ private $hasher;
+ /** @var ICrypto */
+ private $crypto;
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ private $config;
+ /** @var IDBConnection|MockObject */
+ private IDBConnection $db;
+ /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $logger;
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $timeFactory;
+ /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $cacheFactory;
+ /** @var int */
+ private $time;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->mapper = $this->createMock(PublicKeyTokenMapper::class);
+ $this->hasher = Server::get(IHasher::class);
+ $this->crypto = Server::get(ICrypto::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->config->method('getSystemValue')
+ ->willReturnMap([
+ ['openssl', [], []],
+ ]);
+ $this->config->method('getSystemValueString')
+ ->willReturnMap([
+ ['secret', '', '1f4h9s'],
+ ]);
+ $this->db = $this->createMock(IDBConnection::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+ $this->time = 1313131;
+ $this->timeFactory->method('getTime')
+ ->willReturn($this->time);
+ $this->cacheFactory = $this->createMock(ICacheFactory::class);
+
+ $this->tokenProvider = new PublicKeyTokenProvider(
+ $this->mapper,
+ $this->crypto,
+ $this->config,
+ $this->db,
+ $this->logger,
+ $this->timeFactory,
+ $this->hasher,
+ $this->cacheFactory,
+ );
+ }
+
+ public function testGenerateToken(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->assertInstanceOf(PublicKeyToken::class, $actual);
+ $this->assertSame($uid, $actual->getUID());
+ $this->assertSame($user, $actual->getLoginName());
+ $this->assertSame($name, $actual->getName());
+ $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
+ $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
+ }
+
+ public function testGenerateTokenNoPassword(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, false],
+ ]);
+ $this->expectException(PasswordlessTokenException::class);
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->assertInstanceOf(PublicKeyToken::class, $actual);
+ $this->assertSame($uid, $actual->getUID());
+ $this->assertSame($user, $actual->getLoginName());
+ $this->assertSame($name, $actual->getName());
+ $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
+ $this->tokenProvider->getPassword($actual, $token);
+ }
+
+ public function testGenerateTokenLongPassword(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = '';
+ for ($i = 0; $i < 500; $i++) {
+ $password .= 'e';
+ }
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+ $this->expectException(\RuntimeException::class);
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+ }
+
+ public function testGenerateTokenInvalidName(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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'
+ . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
+ . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
+ . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
+ $type = IToken::PERMANENT_TOKEN;
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->assertInstanceOf(PublicKeyToken::class, $actual);
+ $this->assertSame($uid, $actual->getUID());
+ $this->assertSame($user, $actual->getLoginName());
+ $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());
+ $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
+ $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
+ }
+
+ public function testUpdateToken(): void {
+ $tk = new PublicKeyToken();
+ $this->mapper->expects($this->once())
+ ->method('updateActivity')
+ ->with($tk, $this->time);
+ $tk->setLastActivity($this->time - 200);
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+
+ $this->tokenProvider->updateTokenActivity($tk);
+
+ $this->assertEquals($this->time, $tk->getLastActivity());
+ }
+
+ public function testUpdateTokenDebounce(): void {
+ $tk = new PublicKeyToken();
+ $this->config->method('getSystemValueInt')
+ ->willReturnCallback(function ($value, $default) {
+ return $default;
+ });
+ $tk->setLastActivity($this->time - 30);
+
+ $this->mapper->expects($this->never())
+ ->method('updateActivity')
+ ->with($tk, $this->time);
+
+ $this->tokenProvider->updateTokenActivity($tk);
+ }
+
+ public function testGetTokenByUser(): void {
+ $this->mapper->expects($this->once())
+ ->method('getTokenByUser')
+ ->with('uid')
+ ->willReturn(['token']);
+
+ $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid'));
+ }
+
+ public function testGetPassword(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
+ }
+
+
+ public function testGetPasswordPasswordLessToken(): void {
+ $this->expectException(PasswordlessTokenException::class);
+
+ $token = 'token1234';
+ $tk = new PublicKeyToken();
+ $tk->setPassword(null);
+
+ $this->tokenProvider->getPassword($tk, $token);
+ }
+
+
+ public function testGetPasswordInvalidToken(): void {
+ $this->expectException(InvalidTokenException::class);
+
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->tokenProvider->getPassword($actual, 'wrongtoken');
+ }
+
+ public function testSetPassword(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->mapper->method('getTokenByUser')
+ ->with('user')
+ ->willReturn([$actual]);
+
+ $newpass = 'newpass';
+ $this->mapper->expects($this->once())
+ ->method('update')
+ ->with($this->callback(function ($token) use ($newpass) {
+ return $newpass === $this->tokenProvider->getPassword($token, 'tokentokentokentokentoken');
+ }));
+
+
+ $this->tokenProvider->setPassword($actual, $token, $newpass);
+
+ $this->assertSame($newpass, $this->tokenProvider->getPassword($actual, 'tokentokentokentokentoken'));
+ }
+
+
+ public function testSetPasswordInvalidToken(): void {
+ $this->expectException(InvalidTokenException::class);
+
+ $token = $this->createMock(IToken::class);
+ $tokenId = 'token123';
+ $password = '123456';
+
+ $this->tokenProvider->setPassword($token, $tokenId, $password);
+ }
+
+ public function testInvalidateToken(): void {
+ $calls = [
+ [hash('sha512', 'token7' . '1f4h9s')],
+ [hash('sha512', 'token7')]
+ ];
+
+ $this->mapper->expects($this->exactly(2))
+ ->method('invalidate')
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
+
+ $this->tokenProvider->invalidateToken('token7');
+ }
+
+ public function testInvalidateTokenById(): void {
+ $id = 123;
+
+ $this->mapper->expects($this->once())
+ ->method('getTokenById')
+ ->with($id);
+
+ $this->tokenProvider->invalidateTokenById('uid', $id);
+ }
+
+ public function testInvalidateOldTokens(): void {
+ $defaultSessionLifetime = 60 * 60 * 24;
+ $defaultRememberMeLifetime = 60 * 60 * 24 * 15;
+ $wipeTokenLifetime = 60 * 60 * 24 * 60;
+ $this->config->expects($this->exactly(4))
+ ->method('getSystemValueInt')
+ ->willReturnMap([
+ ['session_lifetime', $defaultSessionLifetime, 150],
+ ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300],
+ ['token_auth_wipe_token_retention', $wipeTokenLifetime, 500],
+ ['token_auth_token_retention', 60 * 60 * 24 * 365, 800],
+ ]);
+
+ $calls = [
+ [$this->time - 150, IToken::TEMPORARY_TOKEN, IToken::DO_NOT_REMEMBER],
+ [$this->time - 300, IToken::TEMPORARY_TOKEN, IToken::REMEMBER],
+ [$this->time - 500, IToken::WIPE_TOKEN, null],
+ [$this->time - 800, IToken::PERMANENT_TOKEN, null],
+ ];
+ $this->mapper->expects($this->exactly(4))
+ ->method('invalidateOld')
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
+
+ $this->tokenProvider->invalidateOldTokens();
+ }
+
+ public function testInvalidateLastUsedBefore(): void {
+ $this->mapper->expects($this->once())
+ ->method('invalidateLastUsedBefore')
+ ->with('user', 946684800);
+
+ $this->tokenProvider->invalidateLastUsedBefore('user', 946684800);
+ }
+
+ public function testRenewSessionTokenWithoutPassword(): void {
+ $token = 'oldIdtokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = null;
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->mapper
+ ->expects($this->once())
+ ->method('getToken')
+ ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
+ ->willReturn($oldToken);
+ $this->mapper
+ ->expects($this->once())
+ ->method('insert')
+ ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name) {
+ return $token->getUID() === $uid
+ && $token->getLoginName() === $user
+ && $token->getName() === $name
+ && $token->getType() === IToken::DO_NOT_REMEMBER
+ && $token->getLastActivity() === $this->time
+ && $token->getPassword() === null;
+ }));
+ $this->mapper
+ ->expects($this->once())
+ ->method('delete')
+ ->with($this->callback(function ($token) use ($oldToken) {
+ return $token === $oldToken;
+ }));
+
+ $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
+ }
+
+ public function testRenewSessionTokenWithPassword(): void {
+ $token = 'oldIdtokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'password';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+ $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $this->mapper
+ ->expects($this->once())
+ ->method('getToken')
+ ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
+ ->willReturn($oldToken);
+ $this->mapper
+ ->expects($this->once())
+ ->method('insert')
+ ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name): bool {
+ return $token->getUID() === $uid
+ && $token->getLoginName() === $user
+ && $token->getName() === $name
+ && $token->getType() === IToken::DO_NOT_REMEMBER
+ && $token->getLastActivity() === $this->time
+ && $token->getPassword() !== null
+ && $this->tokenProvider->getPassword($token, 'newIdtokentokentokentoken') === 'password';
+ }));
+ $this->mapper
+ ->expects($this->once())
+ ->method('delete')
+ ->with($this->callback(function ($token) use ($oldToken): bool {
+ return $token === $oldToken;
+ }));
+
+ $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
+ }
+
+ public function testGetToken(): void {
+ $token = new PublicKeyToken();
+
+ $this->config->method('getSystemValue')
+ ->with('secret')
+ ->willReturn('mysecret');
+
+ $this->mapper->method('getToken')
+ ->with(
+ $this->callback(function (string $token) {
+ return hash('sha512', 'unhashedTokentokentokentokentoken' . '1f4h9s') === $token;
+ })
+ )->willReturn($token);
+
+ $this->assertSame($token, $this->tokenProvider->getToken('unhashedTokentokentokentokentoken'));
+ }
+
+ public function testGetInvalidToken(): void {
+ $this->expectException(InvalidTokenException::class);
+
+ $calls = [
+ 'unhashedTokentokentokentokentoken' . '1f4h9s',
+ 'unhashedTokentokentokentokentoken',
+ ];
+ $this->mapper->expects($this->exactly(2))
+ ->method('getToken')
+ ->willReturnCallback(function (string $token) use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals(hash('sha512', $expected), $token);
+ throw new DoesNotExistException('nope');
+ });
+
+ $this->tokenProvider->getToken('unhashedTokentokentokentokentoken');
+ }
+
+ public function testGetExpiredToken(): void {
+ $token = 'tokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'passme';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+ $actual->setExpires(42);
+
+ $this->mapper->method('getToken')
+ ->with(
+ $this->callback(function (string $token) {
+ return hash('sha512', 'tokentokentokentokentoken' . '1f4h9s') === $token;
+ })
+ )->willReturn($actual);
+
+ try {
+ $this->tokenProvider->getToken('tokentokentokentokentoken');
+ $this->fail();
+ } catch (ExpiredTokenException $e) {
+ $this->assertSame($actual, $e->getToken());
+ }
+ }
+
+ public function testGetTokenById(): void {
+ $token = $this->createMock(PublicKeyToken::class);
+
+ $this->mapper->expects($this->once())
+ ->method('getTokenById')
+ ->with($this->equalTo(42))
+ ->willReturn($token);
+
+ $this->assertSame($token, $this->tokenProvider->getTokenById(42));
+ }
+
+ public function testGetInvalidTokenById(): void {
+ $this->expectException(InvalidTokenException::class);
+
+ $this->mapper->expects($this->once())
+ ->method('getTokenById')
+ ->with($this->equalTo(42))
+ ->willThrowException(new DoesNotExistException('nope'));
+
+ $this->tokenProvider->getTokenById(42);
+ }
+
+ public function testGetExpiredTokenById(): void {
+ $token = new PublicKeyToken();
+ $token->setExpires(42);
+
+ $this->mapper->expects($this->once())
+ ->method('getTokenById')
+ ->with($this->equalTo(42))
+ ->willReturn($token);
+
+ try {
+ $this->tokenProvider->getTokenById(42);
+ $this->fail();
+ } catch (ExpiredTokenException $e) {
+ $this->assertSame($token, $e->getToken());
+ }
+ }
+
+ public function testRotate(): void {
+ $token = 'oldtokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = 'password';
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
+
+ $this->assertSame('password', $this->tokenProvider->getPassword($new, 'newtokentokentokentokentoken'));
+ }
+
+ public function testRotateNoPassword(): void {
+ $token = 'oldtokentokentokentokentoken';
+ $uid = 'user';
+ $user = 'User';
+ $password = null;
+ $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';
+ $type = IToken::PERMANENT_TOKEN;
+
+ $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
+
+ $oldPrivate = $actual->getPrivateKey();
+
+ $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
+
+ $newPrivate = $new->getPrivateKey();
+
+ $this->assertNotSame($newPrivate, $oldPrivate);
+ $this->assertNull($new->getPassword());
+ }
+
+ public function testMarkPasswordInvalidInvalidToken(): void {
+ $token = $this->createMock(IToken::class);
+
+ $this->expectException(InvalidTokenException::class);
+
+ $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
+ }
+
+ public function testMarkPasswordInvalid(): void {
+ $token = $this->createMock(PublicKeyToken::class);
+
+ $token->expects($this->once())
+ ->method('setPasswordInvalid')
+ ->with(true);
+ $this->mapper->expects($this->once())
+ ->method('update')
+ ->with($token);
+
+ $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
+ }
+
+ public function testUpdatePasswords(): void {
+ $uid = 'myUID';
+ $token1 = $this->tokenProvider->generateToken(
+ 'foobetokentokentokentoken',
+ $uid,
+ $uid,
+ 'bar',
+ 'random1',
+ IToken::PERMANENT_TOKEN,
+ IToken::REMEMBER);
+ $token2 = $this->tokenProvider->generateToken(
+ 'foobartokentokentokentoken',
+ $uid,
+ $uid,
+ 'bar',
+ 'random2',
+ IToken::PERMANENT_TOKEN,
+ IToken::REMEMBER);
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['auth.storeCryptedPassword', true, true],
+ ]);
+
+ $this->mapper->method('hasExpiredTokens')
+ ->with($uid)
+ ->willReturn(true);
+ $this->mapper->expects($this->once())
+ ->method('getTokenByUser')
+ ->with($uid)
+ ->willReturn([$token1, $token2]);
+ $this->mapper->expects($this->exactly(2))
+ ->method('update')
+ ->with($this->callback(function (PublicKeyToken $t) use ($token1, $token2) {
+ return $t === $token1 || $t === $token2;
+ }));
+
+ $this->tokenProvider->updatePasswords($uid, 'bar2');
+ }
+}
diff --git a/tests/lib/Authentication/Token/PublicKeyTokenTest.php b/tests/lib/Authentication/Token/PublicKeyTokenTest.php
new file mode 100644
index 00000000000..5f5f29c865f
--- /dev/null
+++ b/tests/lib/Authentication/Token/PublicKeyTokenTest.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Authentication\Token;
+
+use OC\Authentication\Token\PublicKeyToken;
+use OCP\Authentication\Token\IToken;
+use Test\TestCase;
+
+class PublicKeyTokenTest extends TestCase {
+ public function testSetScopeAsArray(): void {
+ $scope = [IToken::SCOPE_FILESYSTEM => false];
+ $token = new PublicKeyToken();
+ $token->setScope($scope);
+ $this->assertEquals(json_encode($scope), $token->getScope());
+ $this->assertEquals($scope, $token->getScopeAsArray());
+ }
+
+ public function testDefaultScope(): void {
+ $scope = [IToken::SCOPE_FILESYSTEM => true];
+ $token = new PublicKeyToken();
+ $this->assertEquals($scope, $token->getScopeAsArray());
+ }
+}
diff --git a/tests/lib/Authentication/Token/RemoteWipeTest.php b/tests/lib/Authentication/Token/RemoteWipeTest.php
new file mode 100644
index 00000000000..ca09767c759
--- /dev/null
+++ b/tests/lib/Authentication/Token/RemoteWipeTest.php
@@ -0,0 +1,173 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Authentication\Token;
+
+use OC\Authentication\Events\RemoteWipeFinished;
+use OC\Authentication\Events\RemoteWipeStarted;
+use OC\Authentication\Exceptions\WipeTokenException;
+use OC\Authentication\Token\IProvider;
+use OC\Authentication\Token\IProvider as ITokenProvider;
+use OC\Authentication\Token\IToken;
+use OC\Authentication\Token\IWipeableToken;
+use OC\Authentication\Token\RemoteWipe;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IUser;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class RemoteWipeTest extends TestCase {
+ /** @var ITokenProvider|MockObject */
+ private $tokenProvider;
+
+ /** @var IEventDispatcher|MockObject */
+ private $eventDispatcher;
+
+ /** @var LoggerInterface|MockObject */
+ private $logger;
+
+ /** @var RemoteWipe */
+ private $remoteWipe;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->tokenProvider = $this->createMock(IProvider::class);
+ $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->remoteWipe = new RemoteWipe(
+ $this->tokenProvider,
+ $this->eventDispatcher,
+ $this->logger
+ );
+ }
+
+ public function testMarkNonWipableTokenForWipe(): void {
+ $token = $this->createMock(IToken::class);
+ $result = $this->remoteWipe->markTokenForWipe($token);
+ $this->assertFalse($result);
+ }
+
+ public function testMarkTokenForWipe(): void {
+ $token = $this->createMock(IWipeableToken::class);
+ $token->expects($this->once())
+ ->method('wipe');
+
+ $this->tokenProvider->expects($this->once())
+ ->method('updateToken')
+ ->with($token);
+
+ $result = $this->remoteWipe->markTokenForWipe($token);
+ $this->assertTrue($result);
+ }
+
+ public function testMarkAllTokensForWipeNoWipeableToken(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $token1 = $this->createMock(IToken::class);
+ $token2 = $this->createMock(IToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getTokenByUser')
+ ->with('user123')
+ ->willReturn([$token1, $token2]);
+
+ $result = $this->remoteWipe->markAllTokensForWipe($user);
+
+ $this->assertFalse($result);
+ }
+
+ public function testMarkAllTokensForWipe(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $token1 = $this->createMock(IToken::class);
+ $token2 = $this->createMock(IWipeableToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getTokenByUser')
+ ->with('user123')
+ ->willReturn([$token1, $token2]);
+ $token2->expects($this->once())
+ ->method('wipe');
+ $this->tokenProvider->expects($this->once())
+ ->method('updateToken')
+ ->with($token2);
+
+ $result = $this->remoteWipe->markAllTokensForWipe($user);
+
+ $this->assertTrue($result);
+ }
+
+ public function testStartWipingNotAWipeToken(): void {
+ $token = $this->createMock(IToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getToken')
+ ->with('tk1')
+ ->willReturn($token);
+ $this->eventDispatcher->expects($this->never())
+ ->method('dispatch');
+
+ $result = $this->remoteWipe->start('tk1');
+
+ $this->assertFalse($result);
+ }
+
+ public function testStartWiping(): void {
+ $token = $this->createMock(IToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getToken')
+ ->with('tk1')
+ ->willThrowException(new WipeTokenException($token));
+ $this->eventDispatcher->expects($this->once())
+ ->method('dispatch');
+ $this->eventDispatcher->expects($this->once())
+ ->method('dispatch')
+ ->with(RemoteWipeStarted::class, $this->equalTo(new RemoteWipeStarted($token)));
+
+ $result = $this->remoteWipe->start('tk1');
+
+ $this->assertTrue($result);
+ }
+
+ public function testFinishWipingNotAWipeToken(): void {
+ $token = $this->createMock(IToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getToken')
+ ->with('tk1')
+ ->willReturn($token);
+ $this->eventDispatcher->expects($this->never())
+ ->method('dispatch');
+
+ $result = $this->remoteWipe->finish('tk1');
+
+ $this->assertFalse($result);
+ }
+
+ public function startFinishWiping() {
+ $token = $this->createMock(IToken::class);
+ $this->tokenProvider->expects($this->once())
+ ->method('getToken')
+ ->with('tk1')
+ ->willThrowException(new WipeTokenException($token));
+ $this->eventDispatcher->expects($this->once())
+ ->method('dispatch');
+ $this->tokenProvider->expects($this->once())
+ ->method('invalidateToken')
+ ->with($token);
+ $this->eventDispatcher->expects($this->once())
+ ->method('dispatch')
+ ->with(RemoteWipeFinished::class, $this->equalTo(new RemoteWipeFinished($token)));
+
+ $result = $this->remoteWipe->finish('tk1');
+
+ $this->assertTrue($result);
+ }
+}