diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Settings/Controller/AuthSettingsControllerTest.php | 12 | ||||
-rw-r--r-- | tests/Settings/Controller/CheckSetupControllerTest.php | 85 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php | 12 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/DefaultTokenMapperTest.php | 30 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/DefaultTokenProviderTest.php | 11 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/ManagerTest.php | 451 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php | 250 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php | 506 | ||||
-rw-r--r-- | tests/lib/Authentication/Token/PublicKeyTokenTest.php | 44 | ||||
-rw-r--r-- | tests/lib/AvatarTest.php | 36 | ||||
-rw-r--r-- | tests/lib/Group/GroupTest.php | 10 | ||||
-rw-r--r-- | tests/lib/Http/Client/ClientTest.php | 127 |
12 files changed, 1510 insertions, 64 deletions
diff --git a/tests/Settings/Controller/AuthSettingsControllerTest.php b/tests/Settings/Controller/AuthSettingsControllerTest.php index 461b32b7a48..1c957299e39 100644 --- a/tests/Settings/Controller/AuthSettingsControllerTest.php +++ b/tests/Settings/Controller/AuthSettingsControllerTest.php @@ -75,13 +75,9 @@ class AuthSettingsControllerTest extends TestCase { $sessionToken = new DefaultToken(); $sessionToken->setId(100); - $this->userManager->expects($this->once()) - ->method('get') - ->with($this->uid) - ->will($this->returnValue($this->user)); $this->tokenProvider->expects($this->once()) ->method('getTokenByUser') - ->with($this->user) + ->with($this->uid) ->will($this->returnValue($tokens)); $this->session->expects($this->once()) ->method('getId') @@ -192,13 +188,9 @@ class AuthSettingsControllerTest extends TestCase { $id = 123; $user = $this->createMock(IUser::class); - $this->userManager->expects($this->once()) - ->method('get') - ->with($this->uid) - ->will($this->returnValue($user)); $this->tokenProvider->expects($this->once()) ->method('invalidateTokenById') - ->with($user, $id); + ->with($this->uid, $id); $this->assertEquals([], $this->controller->destroy($id)); } diff --git a/tests/Settings/Controller/CheckSetupControllerTest.php b/tests/Settings/Controller/CheckSetupControllerTest.php index f0e19e007f2..c062dff0704 100644 --- a/tests/Settings/Controller/CheckSetupControllerTest.php +++ b/tests/Settings/Controller/CheckSetupControllerTest.php @@ -21,6 +21,7 @@ namespace Tests\Settings\Controller; +use OC\DB\Connection; use OC\Settings\Controller\CheckSetupController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataDisplayResponse; @@ -28,11 +29,13 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\Http\Client\IClientService; use OCP\IConfig; +use OCP\IDateTimeFormatter; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use OC_Util; +use OCP\Lock\ILockingProvider; use Psr\Http\Message\ResponseInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Test\TestCase; @@ -64,6 +67,12 @@ class CheckSetupControllerTest extends TestCase { private $checker; /** @var EventDispatcher|\PHPUnit_Framework_MockObject_MockObject */ private $dispatcher; + /** @var Connection|\PHPUnit_Framework_MockObject_MockObject */ + private $db; + /** @var ILockingProvider|\PHPUnit_Framework_MockObject_MockObject */ + private $lockingProvider; + /** @var IDateTimeFormatter|\PHPUnit_Framework_MockObject_MockObject */ + private $dateTimeFormatter; public function setUp() { parent::setUp(); @@ -90,6 +99,10 @@ class CheckSetupControllerTest extends TestCase { $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker') ->disableOriginalConstructor()->getMock(); $this->logger = $this->getMockBuilder(ILogger::class)->getMock(); + $this->db = $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor()->getMock(); + $this->lockingProvider = $this->getMockBuilder(ILockingProvider::class)->getMock(); + $this->dateTimeFormatter = $this->getMockBuilder(IDateTimeFormatter::class)->getMock(); $this->checkSetupController = $this->getMockBuilder('\OC\Settings\Controller\CheckSetupController') ->setConstructorArgs([ 'settings', @@ -102,8 +115,11 @@ class CheckSetupControllerTest extends TestCase { $this->checker, $this->logger, $this->dispatcher, + $this->db, + $this->lockingProvider, + $this->dateTimeFormatter, ]) - ->setMethods(['getCurlVersion', 'isPhpOutdated', 'isOpcacheProperlySetup', 'hasFreeTypeSupport', 'hasMissingIndexes', 'isSqliteUsed'])->getMock(); + ->setMethods(['isReadOnlyConfig', 'hasValidTransactionIsolationLevel', 'hasFileinfoInstalled', 'hasWorkingFileLocking', 'getLastCronInfo', 'getSuggestedOverwriteCliURL', 'getOutdatedCaches', 'getCurlVersion', 'isPhpOutdated', 'isOpcacheProperlySetup', 'hasFreeTypeSupport', 'hasMissingIndexes', 'isSqliteUsed'])->getMock(); } public function testIsInternetConnectionWorkingDisabledViaConfig() { @@ -263,21 +279,21 @@ class CheckSetupControllerTest extends TestCase { public function testCheck() { $this->config->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->will($this->returnValue(true)); - $this->config->expects($this->at(1)) + ->method('getAppValue') + ->with('core', 'cronErrors') + ->willReturn(''); + $this->config->expects($this->at(2)) ->method('getSystemValue') ->with('memcache.local', null) ->will($this->returnValue('SomeProvider')); - $this->config->expects($this->at(2)) + $this->config->expects($this->at(3)) ->method('getSystemValue') ->with('has_internet_connection', true) - ->will($this->returnValue(false)); - $this->config->expects($this->at(3)) + ->will($this->returnValue(true)); + $this->config->expects($this->at(4)) ->method('getSystemValue') - ->with('trusted_proxies', []) - ->willReturn(['1.2.3.4']); + ->with('appstoreenabled', true) + ->will($this->returnValue(false)); $this->request->expects($this->once()) ->method('getRemoteAddress') @@ -343,11 +359,55 @@ class CheckSetupControllerTest extends TestCase { ->method('hasMissingIndexes') ->willReturn([]); $this->checkSetupController + ->method('getOutdatedCaches') + ->willReturn([]); + $this->checkSetupController ->method('isSqliteUsed') ->willReturn(false); + $this->checkSetupController + ->expects($this->once()) + ->method('isReadOnlyConfig') + ->willReturn(false); + $this->checkSetupController + ->expects($this->once()) + ->method('hasValidTransactionIsolationLevel') + ->willReturn(true); + $this->checkSetupController + ->expects($this->once()) + ->method('hasFileinfoInstalled') + ->willReturn(true); + $this->checkSetupController + ->expects($this->once()) + ->method('hasWorkingFileLocking') + ->willReturn(true); + $this->checkSetupController + ->expects($this->once()) + ->method('getSuggestedOverwriteCliURL') + ->willReturn(''); + $this->checkSetupController + ->expects($this->once()) + ->method('getLastCronInfo') + ->willReturn([ + 'diffInSeconds' => 123, + 'relativeTime' => '2 hours ago', + 'backgroundJobsUrl' => 'https://example.org', + ]); $expected = new DataResponse( [ + 'isGetenvServerWorking' => true, + 'isReadOnlyConfig' => false, + 'hasValidTransactionIsolationLevel' => true, + 'outdatedCaches' => [], + 'hasFileinfoInstalled' => true, + 'hasWorkingFileLocking' => true, + 'suggestedOverwriteCliURL' => '', + 'cronInfo' => [ + 'diffInSeconds' => 123, + 'relativeTime' => '2 hours ago', + 'backgroundJobsUrl' => 'https://example.org', + ], + 'cronErrors' => '', 'serverHasInternetConnection' => false, 'isMemcacheConfigured' => true, 'memcacheDocs' => 'http://docs.example.org/server/go.php?to=admin-performance', @@ -367,9 +427,9 @@ class CheckSetupControllerTest extends TestCase { 'phpOpcacheDocumentation' => 'http://docs.example.org/server/go.php?to=admin-php-opcache', 'isSettimelimitAvailable' => true, 'hasFreeTypeSupport' => false, - 'hasMissingIndexes' => [], 'isSqliteUsed' => false, 'databaseConversionDocumentation' => 'http://docs.example.org/server/go.php?to=admin-db-conversion', + 'missingIndexes' => [], ] ); $this->assertEquals($expected, $this->checkSetupController->check()); @@ -388,6 +448,9 @@ class CheckSetupControllerTest extends TestCase { $this->checker, $this->logger, $this->dispatcher, + $this->db, + $this->lockingProvider, + $this->dateTimeFormatter, ]) ->setMethods(null)->getMock(); diff --git a/tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php b/tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php index c9082c08b30..b8074d75b30 100644 --- a/tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php @@ -23,6 +23,8 @@ namespace Test\Authentication\Token; use OC\Authentication\Token\DefaultTokenCleanupJob; +use OC\Authentication\Token\IProvider; +use OC\Authentication\Token\Manager; use Test\TestCase; class DefaultTokenCleanupJobTest extends TestCase { @@ -34,19 +36,13 @@ class DefaultTokenCleanupJobTest extends TestCase { protected function setUp() { parent::setUp(); - $this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider') + $this->tokenProvider = $this->getMockBuilder(Manager::class) ->disableOriginalConstructor() ->getMock(); - $this->overwriteService('\OC\Authentication\Token\DefaultTokenProvider', $this->tokenProvider); + $this->overwriteService(IProvider::class, $this->tokenProvider); $this->job = new DefaultTokenCleanupJob(); } - protected function tearDown() { - parent::tearDown(); - - $this->restoreService('\OC\Authentication\Token\DefaultTokenProvider'); - } - public function testRun() { $this->tokenProvider->expects($this->once()) ->method('invalidateOldTokens') diff --git a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php index b5d24a7ab5e..bebceba62cf 100644 --- a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php @@ -135,6 +135,7 @@ class DefaultTokenMapperTest extends TestCase { $token->setRemember(IToken::DO_NOT_REMEMBER); $token->setLastActivity($this->time - 60 * 60 * 24 * 3); $token->setLastCheck($this->time - 10); + $token->setVersion(DefaultToken::VERSION); $dbToken = $this->mapper->getToken($token->getToken()); @@ -164,6 +165,7 @@ class DefaultTokenMapperTest extends TestCase { $token->setRemember(IToken::DO_NOT_REMEMBER); $token->setLastActivity($this->time - 60 * 60 * 24 * 3); $token->setLastCheck($this->time - 10); + $token->setVersion(DefaultToken::VERSION); $dbToken = $this->mapper->getToken($token->getToken()); $token->setId($dbToken->getId()); // We don't know the ID @@ -190,23 +192,11 @@ class DefaultTokenMapperTest extends TestCase { } public function testGetTokenByUser() { - /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock(IUser::class); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('user1')); - - $this->assertCount(2, $this->mapper->getTokenByUser($user)); + $this->assertCount(2, $this->mapper->getTokenByUser('user1')); } public function testGetTokenByUserNotFound() { - /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock(IUser::class); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('user1000')); - - $this->assertCount(0, $this->mapper->getTokenByUser($user)); + $this->assertCount(0, $this->mapper->getTokenByUser('user1000')); } public function testDeleteById() { @@ -218,23 +208,15 @@ class DefaultTokenMapperTest extends TestCase { ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'))); $result = $qb->execute(); $id = $result->fetch()['id']; - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('user1')); - $this->mapper->deleteById($user, $id); + $this->mapper->deleteById('user1', $id); $this->assertEquals(2, $this->getNumberOfTokens()); } public function testDeleteByIdWrongUser() { - /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock(IUser::class); $id = 33; - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('user10000')); - $this->mapper->deleteById($user, $id); + $this->mapper->deleteById('user1000', $id); $this->assertEquals(3, $this->getNumberOfTokens()); } diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php index 95b5b928559..3fb11f410ba 100644 --- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php @@ -91,6 +91,7 @@ class DefaultTokenProviderTest extends TestCase { $toInsert->setRemember(IToken::DO_NOT_REMEMBER); $toInsert->setLastActivity($this->time); $toInsert->setLastCheck($this->time); + $toInsert->setVersion(DefaultToken::VERSION); $this->config->expects($this->any()) ->method('getSystemValue') @@ -132,13 +133,12 @@ class DefaultTokenProviderTest extends TestCase { } public function testGetTokenByUser() { - $user = $this->createMock(IUser::class); $this->mapper->expects($this->once()) ->method('getTokenByUser') - ->with($user) + ->with('uid') ->will($this->returnValue(['token'])); - $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser($user)); + $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid')); } public function testGetPassword() { @@ -243,13 +243,12 @@ class DefaultTokenProviderTest extends TestCase { public function testInvaildateTokenById() { $id = 123; - $user = $this->createMock(IUser::class); $this->mapper->expects($this->once()) ->method('deleteById') - ->with($user, $id); + ->with('uid', $id); - $this->tokenProvider->invalidateTokenById($user, $id); + $this->tokenProvider->invalidateTokenById('uid', $id); } public function testInvalidateOldTokens() { diff --git a/tests/lib/Authentication/Token/ManagerTest.php b/tests/lib/Authentication/Token/ManagerTest.php new file mode 100644 index 00000000000..8b77bfc4994 --- /dev/null +++ b/tests/lib/Authentication/Token/ManagerTest.php @@ -0,0 +1,451 @@ +<?php +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Authentication\Token; + +use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\PasswordlessTokenException; +use OC\Authentication\Token\DefaultToken; +use OC\Authentication\Token\DefaultTokenProvider; +use OC\Authentication\Token\Manager; +use OC\Authentication\Token\PublicKeyToken; +use OC\Authentication\Token\PublicKeyTokenMapper; +use OC\Authentication\Token\PublicKeyTokenProvider; +use OC\Authentication\Token\ExpiredTokenException; +use OC\Authentication\Token\IToken; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IUser; +use OCP\Security\ICrypto; +use Test\TestCase; + +class ManagerTest extends TestCase { + + /** @var PublicKeyTokenProvider|\PHPUnit_Framework_MockObject_MockObject */ + private $publicKeyTokenProvider; + /** @var DefaultTokenProvider|\PHPUnit_Framework_MockObject_MockObject */ + private $defaultTokenProvider; + /** @var Manager */ + private $manager; + + protected function setUp() { + parent::setUp(); + + $this->publicKeyTokenProvider = $this->createMock(PublicKeyTokenProvider::class); + $this->defaultTokenProvider = $this->createMock(DefaultTokenProvider::class); + $this->manager = new Manager( + $this->defaultTokenProvider, + $this->publicKeyTokenProvider + ); + } + + public function testGenerateToken() { + $this->defaultTokenProvider->expects($this->never()) + ->method('generateToken'); + + $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 tokenData(): array { + return [ + [new DefaultToken()], + [new PublicKeyToken()], + [$this->createMock(IToken::class)], + ]; + } + + protected function setNoCall(IToken $token) { + if (!($token instanceof DefaultToken)) { + $this->defaultTokenProvider->expects($this->never()) + ->method($this->anything()); + } + + if (!($token instanceof PublicKeyToken)) { + $this->publicKeyTokenProvider->expects($this->never()) + ->method($this->anything()); + } + } + + protected function setCall(IToken $token, string $function, $return = null) { + if ($token instanceof DefaultToken) { + $this->defaultTokenProvider->expects($this->once()) + ->method($function) + ->with($token) + ->willReturn($return); + } + + if ($token instanceof PublicKeyToken) { + $this->publicKeyTokenProvider->expects($this->once()) + ->method($function) + ->with($token) + ->willReturn($return); + } + } + + protected function setException(IToken $token) { + if (!($token instanceof DefaultToken) && !($token instanceof PublicKeyToken)) { + $this->expectException(InvalidTokenException::class); + } + } + + /** + * @dataProvider tokenData + */ + public function testUpdateToken(IToken $token) { + $this->setNoCall($token); + $this->setCall($token, 'updateToken'); + $this->setException($token); + + $this->manager->updateToken($token); + } + + /** + * @dataProvider tokenData + */ + public function testUpdateTokenActivity(IToken $token) { + $this->setNoCall($token); + $this->setCall($token, 'updateTokenActivity'); + $this->setException($token); + + $this->manager->updateTokenActivity($token); + } + + /** + * @dataProvider tokenData + */ + public function testGetPassword(IToken $token) { + $this->setNoCall($token); + $this->setCall($token, 'getPassword', 'password'); + $this->setException($token); + + $result = $this->manager->getPassword($token, 'tokenId', 'password'); + + $this->assertSame('password', $result); + } + + /** + * @dataProvider tokenData + */ + public function testSetPassword(IToken $token) { + $this->setNoCall($token); + $this->setCall($token, 'setPassword'); + $this->setException($token); + + $this->manager->setPassword($token, 'tokenId', 'password'); + } + + public function testInvalidateTokens() { + $this->defaultTokenProvider->expects($this->once()) + ->method('invalidateToken') + ->with('token'); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('invalidateToken') + ->with('token'); + + $this->manager->invalidateToken('token'); + } + + public function testInvalidateTokenById() { + $this->defaultTokenProvider->expects($this->once()) + ->method('invalidateTokenById') + ->with('uid', 42); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('invalidateTokenById') + ->with('uid', 42); + + $this->manager->invalidateTokenById('uid', 42); + } + + public function testInvalidateOldTokens() { + $this->defaultTokenProvider->expects($this->once()) + ->method('invalidateOldTokens'); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('invalidateOldTokens'); + + $this->manager->invalidateOldTokens(); + } + + public function testGetTokenByUser() { + $t1 = new DefaultToken(); + $t2 = new DefaultToken(); + $t3 = new PublicKeyToken(); + $t4 = new PublicKeyToken(); + + $this->defaultTokenProvider + ->method('getTokenByUser') + ->willReturn([$t1, $t2]); + + $this->publicKeyTokenProvider + ->method('getTokenByUser') + ->willReturn([$t3, $t4]); + + $result = $this->manager->getTokenByUser('uid'); + + $this->assertEquals([$t1, $t2, $t3, $t4], $result); + } + + public function testRenewSessionTokenPublicKey() { + $this->defaultTokenProvider->expects($this->never()) + ->method($this->anything()); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with('oldId', 'newId'); + + $this->manager->renewSessionToken('oldId', 'newId'); + } + + public function testRenewSessionTokenDefault() { + $this->publicKeyTokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with('oldId', 'newId') + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with('oldId', 'newId'); + + $this->manager->renewSessionToken('oldId', 'newId'); + } + + public function testRenewSessionInvalid() { + $this->publicKeyTokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with('oldId', 'newId') + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with('oldId', 'newId') + ->willThrowException(new InvalidTokenException()); + + $this->expectException(InvalidTokenException::class); + $this->manager->renewSessionToken('oldId', 'newId'); + } + + public function testGetTokenByIdPublicKey() { + $token = $this->createMock(IToken::class); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('getTokenById') + ->with(42) + ->willReturn($token); + + $this->defaultTokenProvider->expects($this->never()) + ->method($this->anything()); + + + $this->assertSame($token, $this->manager->getTokenById(42)); + } + + public function testGetTokenByIdDefault() { + $token = $this->createMock(IToken::class); + + $this->publicKeyTokenProvider->expects($this->once()) + ->method('getTokenById') + ->with(42) + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider->expects($this->once()) + ->method('getTokenById') + ->with(42) + ->willReturn($token); + + $this->assertSame($token, $this->manager->getTokenById(42)); + } + + public function testGetTokenByIdInvalid() { + $this->publicKeyTokenProvider->expects($this->once()) + ->method('getTokenById') + ->with(42) + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider->expects($this->once()) + ->method('getTokenById') + ->with(42) + ->willThrowException(new InvalidTokenException()); + + $this->expectException(InvalidTokenException::class); + $this->manager->getTokenById(42); + } + + public function testGetTokenPublicKey() { + $token = new PublicKeyToken(); + + $this->defaultTokenProvider->expects($this->never()) + ->method($this->anything()); + + $this->publicKeyTokenProvider + ->method('getToken') + ->with('tokenId') + ->willReturn($token); + + $this->assertSame($token, $this->manager->getToken('tokenId')); + } + + public function testGetTokenInvalid() { + $this->defaultTokenProvider + ->method('getToken') + ->with('tokenId') + ->willThrowException(new InvalidTokenException()); + + $this->publicKeyTokenProvider + ->method('getToken') + ->with('tokenId') + ->willThrowException(new InvalidTokenException()); + + $this->expectException(InvalidTokenException::class); + $this->manager->getToken('tokenId'); + } + + public function testGetTokenConvertPassword() { + $oldToken = new DefaultToken(); + $newToken = new PublicKeyToken(); + + $this->publicKeyTokenProvider + ->method('getToken') + ->with('tokenId') + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider + ->method('getToken') + ->willReturn($oldToken); + + $this->defaultTokenProvider + ->method('getPassword') + ->with($oldToken, 'tokenId') + ->willReturn('password'); + + $this->publicKeyTokenProvider + ->method('convertToken') + ->with($oldToken, 'tokenId', 'password') + ->willReturn($newToken); + + $this->assertSame($newToken, $this->manager->getToken('tokenId')); + } + + public function testGetTokenConvertNoPassword() { + $oldToken = new DefaultToken(); + $newToken = new PublicKeyToken(); + + $this->publicKeyTokenProvider + ->method('getToken') + ->with('tokenId') + ->willThrowException(new InvalidTokenException()); + + $this->defaultTokenProvider + ->method('getToken') + ->willReturn($oldToken); + + $this->defaultTokenProvider + ->method('getPassword') + ->with($oldToken, 'tokenId') + ->willThrowException(new PasswordlessTokenException()); + + $this->publicKeyTokenProvider + ->method('convertToken') + ->with($oldToken, 'tokenId', null) + ->willReturn($newToken); + + $this->assertSame($newToken, $this->manager->getToken('tokenId')); + } + + public function testRotateInvalid() { + $this->expectException(InvalidTokenException::class); + $this->manager->rotate($this->createMock(IToken::class), 'oldId', 'newId'); + } + + public function testRotatePublicKey() { + $token = new PublicKeyToken(); + + $this->publicKeyTokenProvider + ->method('rotate') + ->with($token, 'oldId', 'newId') + ->willReturn($token); + + $this->assertSame($token, $this->manager->rotate($token, 'oldId', 'newId')); + } + + public function testRotateConvertPassword() { + $oldToken = new DefaultToken(); + $newToken = new PublicKeyToken(); + + $this->defaultTokenProvider + ->method('getPassword') + ->with($oldToken, 'oldId') + ->willReturn('password'); + + $this->publicKeyTokenProvider + ->method('convertToken') + ->with($oldToken, 'newId', 'password') + ->willReturn($newToken); + + $this->assertSame($newToken, $this->manager->rotate($oldToken, 'oldId', 'newId')); + } + + public function testRotateConvertNoPassword() { + $oldToken = new DefaultToken(); + $newToken = new PublicKeyToken(); + + $this->defaultTokenProvider + ->method('getPassword') + ->with($oldToken, 'oldId') + ->willThrowException(new PasswordlessTokenException()); + + $this->publicKeyTokenProvider + ->method('convertToken') + ->with($oldToken, 'newId', null) + ->willReturn($newToken); + + $this->assertSame($newToken, $this->manager->rotate($oldToken, 'oldId', 'newId')); + } +} diff --git a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php new file mode 100644 index 00000000000..5a98747ab0d --- /dev/null +++ b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php @@ -0,0 +1,250 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Authentication\Token; + +use OC; +use OC\Authentication\Token\PublicKeyToken; +use OC\Authentication\Token\PublicKeyTokenMapper; +use OC\Authentication\Token\IToken; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\IUser; +use Test\TestCase; + +/** + * @group DB + */ +class PublicKeyTokenMapperTest extends TestCase { + + /** @var PublicKeyTokenMapper */ + private $mapper; + + /** @var IDBConnection */ + private $dbConnection; + + /** @var int */ + private $time; + + protected function setUp() { + parent::setUp(); + + $this->dbConnection = OC::$server->getDatabaseConnection(); + $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(); + } + + private function getNumberOfTokens() { + $qb = $this->dbConnection->getQueryBuilder(); + $result = $qb->select($qb->createFunction('count(*) as `count`')) + ->from('authtoken') + ->execute() + ->fetch(); + return (int) $result['count']; + } + + public function testInvalidate() { + $token = '9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'; + + $this->mapper->invalidate($token); + + $this->assertSame(2, $this->getNumberOfTokens()); + } + + public function testInvalidateInvalid() { + $token = 'youwontfindthisoneinthedatabase'; + + $this->mapper->invalidate($token); + + $this->assertSame(3, $this->getNumberOfTokens()); + } + + public function testInvalidateOld() { + $olderThan = $this->time - 60 * 60; // One hour + + $this->mapper->invalidateOld($olderThan); + + $this->assertSame(2, $this->getNumberOfTokens()); + } + + public function testGetToken() { + $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); + } + + /** + * @expectedException \OCP\AppFramework\Db\DoesNotExistException + */ + public function testGetInvalidToken() { + $token = 'thisisaninvalidtokenthatisnotinthedatabase'; + + $this->mapper->getToken($token); + } + + public function testGetTokenById() { + $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); + } + + /** + * @expectedException \OCP\AppFramework\Db\DoesNotExistException + */ + public function testGetTokenByIdNotFound() { + $this->mapper->getTokenById(-1); + } + + /** + * @expectedException \OCP\AppFramework\Db\DoesNotExistException + */ + public function testGetInvalidTokenById() { + $id = '42'; + + $this->mapper->getToken($id); + } + + public function testGetTokenByUser() { + $this->assertCount(2, $this->mapper->getTokenByUser('user1')); + } + + public function testGetTokenByUserNotFound() { + $this->assertCount(0, $this->mapper->getTokenByUser('user1000')); + } + + public function testDeleteById() { + /** @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']; + + $this->mapper->deleteById('user1', (int)$id); + $this->assertEquals(2, $this->getNumberOfTokens()); + } + + public function testDeleteByIdWrongUser() { + /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ + $user = $this->createMock(IUser::class); + $id = 33; + + $this->mapper->deleteById('user1000', $id); + $this->assertEquals(3, $this->getNumberOfTokens()); + } + + public function testDeleteByName() { + $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(2, $this->getNumberOfTokens()); + } + +} diff --git a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php new file mode 100644 index 00000000000..cd3bcb81ba6 --- /dev/null +++ b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php @@ -0,0 +1,506 @@ +<?php +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Authentication\Token; + +use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\PasswordlessTokenException; +use OC\Authentication\Token\DefaultToken; +use OC\Authentication\Token\PublicKeyToken; +use OC\Authentication\Token\PublicKeyTokenMapper; +use OC\Authentication\Token\PublicKeyTokenProvider; +use OC\Authentication\Token\ExpiredTokenException; +use OC\Authentication\Token\IToken; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IUser; +use OCP\Security\ICrypto; +use Test\TestCase; + +class PublicKeyTokenProviderTest extends TestCase { + + /** @var PublicKeyTokenProvider|\PHPUnit_Framework_MockObject_MockObject */ + private $tokenProvider; + /** @var PublicKeyTokenMapper|\PHPUnit_Framework_MockObject_MockObject */ + private $mapper; + /** @var ICrypto */ + private $crypto; + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $config; + /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ + private $logger; + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $timeFactory; + /** @var int */ + private $time; + + protected function setUp() { + parent::setUp(); + + $this->mapper = $this->createMock(PublicKeyTokenMapper::class); + $this->crypto = \OC::$server->getCrypto(); + $this->config = $this->createMock(IConfig::class); + $this->config->method('getSystemValue') + ->will($this->returnValueMap([ + ['session_lifetime', 60 * 60 * 24, 150], + ['remember_login_cookie_lifetime', 60 * 60 * 24 * 15, 300], + ['secret', '', '1f4h9s'], + ])); + $this->logger = $this->createMock(ILogger::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->time = 1313131; + $this->timeFactory->method('getTime') + ->willReturn($this->time); + + $this->tokenProvider = new PublicKeyTokenProvider($this->mapper, $this->crypto, $this->config, $this->logger, + $this->timeFactory); + } + + public function testGenerateToken() { + $token = 'token'; + $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; + + $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 testUpdateToken() { + $tk = new PublicKeyToken(); + $tk->setLastActivity($this->time - 200); + $this->mapper->expects($this->once()) + ->method('update') + ->with($tk); + + $this->tokenProvider->updateTokenActivity($tk); + + $this->assertEquals($this->time, $tk->getLastActivity()); + } + + public function testUpdateTokenDebounce() { + $tk = new PublicKeyToken(); + $tk->setLastActivity($this->time - 30); + $this->mapper->expects($this->never()) + ->method('update') + ->with($tk); + + $this->tokenProvider->updateTokenActivity($tk); + } + + public function testGetTokenByUser() { + $this->mapper->expects($this->once()) + ->method('getTokenByUser') + ->with('uid') + ->will($this->returnValue(['token'])); + + $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid')); + } + + public function testGetPassword() { + $token = 'token'; + $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; + + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token)); + } + + /** + * @expectedException \OC\Authentication\Exceptions\PasswordlessTokenException + */ + public function testGetPasswordPasswordLessToken() { + $token = 'token1234'; + $tk = new PublicKeyToken(); + $tk->setPassword(null); + + $this->tokenProvider->getPassword($tk, $token); + } + + /** + * @expectedException \OC\Authentication\Exceptions\InvalidTokenException + */ + public function testGetPasswordInvalidToken() { + $token = 'token'; + $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; + + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $this->tokenProvider->getPassword($actual, 'wrongtoken'); + } + + public function testSetPassword() { + $token = 'token'; + $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; + + $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, 'token'); + })); + + + $this->tokenProvider->setPassword($actual, $token, $newpass); + + $this->assertSame($newpass, $this->tokenProvider->getPassword($actual, 'token')); + } + + /** + * @expectedException \OC\Authentication\Exceptions\InvalidTokenException + */ + public function testSetPasswordInvalidToken() { + $token = $this->createMock(IToken::class); + $tokenId = 'token123'; + $password = '123456'; + + $this->tokenProvider->setPassword($token, $tokenId, $password); + } + + public function testInvalidateToken() { + $this->mapper->expects($this->once()) + ->method('invalidate') + ->with(hash('sha512', 'token7'.'1f4h9s')); + + $this->tokenProvider->invalidateToken('token7'); + } + + public function testInvaildateTokenById() { + $id = 123; + + $this->mapper->expects($this->once()) + ->method('deleteById') + ->with('uid', $id); + + $this->tokenProvider->invalidateTokenById('uid', $id); + } + + public function testInvalidateOldTokens() { + $defaultSessionLifetime = 60 * 60 * 24; + $defaultRememberMeLifetime = 60 * 60 * 24 * 15; + $this->config->expects($this->exactly(2)) + ->method('getSystemValue') + ->will($this->returnValueMap([ + ['session_lifetime', $defaultSessionLifetime, 150], + ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300], + ])); + $this->mapper->expects($this->at(0)) + ->method('invalidateOld') + ->with($this->time - 150); + $this->mapper->expects($this->at(1)) + ->method('invalidateOld') + ->with($this->time - 300); + + $this->tokenProvider->invalidateOldTokens(); + } + + public function testRenewSessionTokenWithoutPassword() { + $token = 'oldId'; + $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' + . '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; + + $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $this->mapper + ->expects($this->at(0)) + ->method('getToken') + ->with(hash('sha512', 'oldId' . '1f4h9s')) + ->willReturn($oldToken); + $this->mapper + ->expects($this->at(1)) + ->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->at(2)) + ->method('delete') + ->with($this->callback(function($token) use ($oldToken) { + return $token === $oldToken; + })); + + $this->tokenProvider->renewSessionToken('oldId', 'newId'); + } + + public function testRenewSessionTokenWithPassword() { + $token = 'oldId'; + $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' + . '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; + + $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $this->mapper + ->expects($this->at(0)) + ->method('getToken') + ->with(hash('sha512', 'oldId' . '1f4h9s')) + ->willReturn($oldToken); + $this->mapper + ->expects($this->at(1)) + ->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->tokenProvider->getPassword($token, 'newId') === 'password'; + })); + $this->mapper + ->expects($this->at(2)) + ->method('delete') + ->with($this->callback(function($token) use ($oldToken) { + return $token === $oldToken; + })); + + $this->tokenProvider->renewSessionToken('oldId', 'newId'); + } + + public function testGetToken() { + $token = new PublicKeyToken(); + + $this->config->method('getSystemValue') + ->with('secret') + ->willReturn('mysecret'); + + $this->mapper->method('getToken') + ->with( + $this->callback(function (string $token) { + return hash('sha512', 'unhashedToken'.'1f4h9s') === $token; + }) + )->willReturn($token); + + $this->assertSame($token, $this->tokenProvider->getToken('unhashedToken')); + } + + public function testGetInvalidToken() { + $this->expectException(InvalidTokenException::class); + + $this->mapper->method('getToken') + ->with( + $this->callback(function (string $token) { + return hash('sha512', 'unhashedToken'.'1f4h9s') === $token; + }) + )->willThrowException(new DoesNotExistException('nope')); + + $this->tokenProvider->getToken('unhashedToken'); + } + + public function testGetExpiredToken() { + $token = 'token'; + $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; + + $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', 'token'.'1f4h9s') === $token; + }) + )->willReturn($actual); + + try { + $this->tokenProvider->getToken('token'); + $this->fail(); + } catch (ExpiredTokenException $e) { + $this->assertSame($actual, $e->getToken()); + } + + } + + public function testGetTokenById() { + $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() { + $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() { + $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() { + $token = 'oldtoken'; + $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' + . '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; + + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $new = $this->tokenProvider->rotate($actual, 'oldtoken', 'newtoken'); + + $this->assertSame('password', $this->tokenProvider->getPassword($new, 'newtoken')); + } + + public function testRotateNoPassword() { + $token = 'oldtoken'; + $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' + . '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; + + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); + + $oldPrivate = $actual->getPrivateKey(); + + $new = $this->tokenProvider->rotate($actual, 'oldtoken', 'newtoken'); + + $newPrivate = $new->getPrivateKey(); + + $this->assertNotSame($newPrivate, $oldPrivate); + $this->assertNull($new->getPassword()); + } + + public function testConvertToken() { + $defaultToken = new DefaultToken(); + $defaultToken->setId(42); + $defaultToken->setPassword('oldPass'); + $defaultToken->setExpires(1337); + $defaultToken->setToken('oldToken'); + $defaultToken->setUid('uid'); + $defaultToken->setLoginName('loginName'); + $defaultToken->setLastActivity(999); + $defaultToken->setName('name'); + $defaultToken->setRemember(IToken::REMEMBER); + $defaultToken->setType(IToken::PERMANENT_TOKEN); + + $this->mapper->expects($this->once()) + ->method('update') + ->willReturnArgument(0); + + $newToken = $this->tokenProvider->convertToken($defaultToken, 'newToken', 'newPassword'); + + $this->assertSame(42, $newToken->getId()); + $this->assertSame('newPassword', $this->tokenProvider->getPassword($newToken, 'newToken')); + $this->assertSame(1337, $newToken->getExpires()); + $this->assertSame('uid', $newToken->getUID()); + $this->assertSame('loginName', $newToken->getLoginName()); + $this->assertSame(1313131, $newToken->getLastActivity()); + $this->assertSame(1313131, $newToken->getLastCheck()); + $this->assertSame('name', $newToken->getName()); + $this->assertSame(IToken::REMEMBER, $newToken->getRemember()); + $this->assertSame(IToken::PERMANENT_TOKEN, $newToken->getType()); + } +} diff --git a/tests/lib/Authentication/Token/PublicKeyTokenTest.php b/tests/lib/Authentication/Token/PublicKeyTokenTest.php new file mode 100644 index 00000000000..d0226eb9902 --- /dev/null +++ b/tests/lib/Authentication/Token/PublicKeyTokenTest.php @@ -0,0 +1,44 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Authentication\Token; + +use OC\Authentication\Token\PublicKeyToken; +use Test\TestCase; + +class PublicKeyTokenTest extends TestCase { + public function testSetScopeAsArray() { + $scope = ['filesystem' => false]; + $token = new PublicKeyToken(); + $token->setScope($scope); + $this->assertEquals(json_encode($scope), $token->getScope()); + $this->assertEquals($scope, $token->getScopeAsArray()); + } + + public function testDefaultScope() { + $scope = ['filesystem' => true]; + $token = new PublicKeyToken(); + $this->assertEquals($scope, $token->getScopeAsArray()); + } +} diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index 4914c02bd14..759dd385564 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -48,6 +48,9 @@ class AvatarTest extends \Test\TestCase { $this->createMock(ILogger::class), $this->config ); + + // abcdefghi is a convenient name that our algorithm convert to our nextcloud blue 0082c9 + $this->user->method('getDisplayName')->willReturn('abcdefghi'); } public function testGetNoAvatar() { @@ -226,4 +229,37 @@ class AvatarTest extends \Test\TestCase { $this->avatar->set($image->data()); } + public function testGenerateSvgAvatar() { + $avatar = $this->invokePrivate($this->avatar, 'getAvatarVector', [64]); + + $svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?> + <svg width="64" height="64" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg"> + <rect width="100%" height="100%" fill="#0082c9"></rect> + <text x="50%" y="350" style="font-weight:600;font-size:278px;font-family:\'Open Sans\';text-anchor:middle;fill:#fff">A</text> + </svg>'; + $this->assertEquals($avatar, $svg); + } + + public function testHashToInt() { + $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]); + $this->assertTrue(gettype($hashToInt) === 'integer'); + } + + public function testMixPalette() { + $colorFrom = new \OC\Color(0,0,0); + $colorTo = new \OC\Color(6,12,18); + $steps = 6; + $palette = $this->invokePrivate($this->avatar, 'mixPalette', [$steps, $colorFrom, $colorTo]); + foreach($palette as $j => $color) { + // calc increment + $incR = $colorTo->r / $steps * $j; + $incG = $colorTo->g / $steps * $j; + $incB = $colorTo->b / $steps * $j; + // ensure everything is equal + $this->assertEquals($color, new \OC\Color($incR, $incG,$incB)); + } + $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]); + $this->assertTrue(gettype($hashToInt) === 'integer'); + } + } diff --git a/tests/lib/Group/GroupTest.php b/tests/lib/Group/GroupTest.php index c7cbbc2321b..a0b77bbe4d7 100644 --- a/tests/lib/Group/GroupTest.php +++ b/tests/lib/Group/GroupTest.php @@ -303,7 +303,7 @@ class GroupTest extends \Test\TestCase { $users = $group->searchUsers('2'); $this->assertEquals(1, count($users)); - $user2 = $users[0]; + $user2 = $users['user2']; $this->assertEquals('user2', $user2->getUID()); } @@ -329,7 +329,7 @@ class GroupTest extends \Test\TestCase { $users = $group->searchUsers('2'); $this->assertEquals(1, count($users)); - $user2 = $users[0]; + $user2 = $users['user2']; $this->assertEquals('user2', $user2->getUID()); } @@ -348,7 +348,7 @@ class GroupTest extends \Test\TestCase { $users = $group->searchUsers('user', 1, 1); $this->assertEquals(1, count($users)); - $user2 = $users[0]; + $user2 = $users['user2']; $this->assertEquals('user2', $user2->getUID()); } @@ -374,8 +374,8 @@ class GroupTest extends \Test\TestCase { $users = $group->searchUsers('user', 2, 1); $this->assertEquals(2, count($users)); - $user2 = $users[0]; - $user1 = $users[1]; + $user2 = $users['user2']; + $user1 = $users['user1']; $this->assertEquals('user2', $user2->getUID()); $this->assertEquals('user1', $user1->getUID()); } diff --git a/tests/lib/Http/Client/ClientTest.php b/tests/lib/Http/Client/ClientTest.php index ec4ca6ec90c..7f12a824d17 100644 --- a/tests/lib/Http/Client/ClientTest.php +++ b/tests/lib/Http/Client/ClientTest.php @@ -27,6 +27,8 @@ class ClientTest extends \Test\TestCase { private $client; /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ private $config; + /** @var array */ + private $defaultRequestOptions; public function setUp() { parent::setUp(); @@ -85,42 +87,167 @@ class ClientTest extends \Test\TestCase { $this->assertSame('username:password@foo', self::invokePrivate($this->client, 'getProxyUri')); } + private function setUpDefaultRequestOptions() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('proxy', null) + ->willReturn('foo'); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('proxyuserpwd', null) + ->willReturn(null); + $this->certificateManager + ->expects($this->once()) + ->method('getAbsoluteBundlePath') + ->with(null) + ->willReturn('/my/path.crt'); + + $this->defaultRequestOptions = [ + 'verify' => '/my/path.crt', + 'proxy' => 'foo' + ]; + } + public function testGet() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('get', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->get('http://localhost/', [])->getStatusCode()); } + public function testGetWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('get', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->get('http://localhost/', $options)->getStatusCode()); + } + public function testPost() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('post', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->post('http://localhost/', [])->getStatusCode()); } + public function testPostWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('post', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->post('http://localhost/', $options)->getStatusCode()); + } + public function testPut() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('put', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->put('http://localhost/', [])->getStatusCode()); } + public function testPutWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('put', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->put('http://localhost/', $options)->getStatusCode()); + } + public function testDelete() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('delete', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->delete('http://localhost/', [])->getStatusCode()); } + public function testDeleteWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('delete', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->delete('http://localhost/', $options)->getStatusCode()); + } + public function testOptions() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('options', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->options('http://localhost/', [])->getStatusCode()); } + public function testOptionsWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('options', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->options('http://localhost/', $options)->getStatusCode()); + } + public function testHead() { + $this->setUpDefaultRequestOptions(); + $this->guzzleClient->method('request') + ->with('head', 'http://localhost/', $this->defaultRequestOptions) ->willReturn(new Response(1337)); $this->assertEquals(1337, $this->client->head('http://localhost/', [])->getStatusCode()); } + public function testHeadWithOptions() { + $this->setUpDefaultRequestOptions(); + + $options = [ + 'verify' => false, + 'proxy' => 'bar' + ]; + + $this->guzzleClient->method('request') + ->with('head', 'http://localhost/', $options) + ->willReturn(new Response(1337)); + $this->assertEquals(1337, $this->client->head('http://localhost/', $options)->getStatusCode()); + } + public function testSetDefaultOptionsWithNotInstalled() { $this->config ->expects($this->at(0)) |