diff options
Diffstat (limited to 'apps/encryption/tests')
26 files changed, 2602 insertions, 2640 deletions
diff --git a/apps/encryption/tests/Command/FixEncryptedVersionTest.php b/apps/encryption/tests/Command/FixEncryptedVersionTest.php new file mode 100644 index 00000000000..d0af359183b --- /dev/null +++ b/apps/encryption/tests/Command/FixEncryptedVersionTest.php @@ -0,0 +1,390 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2019 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace OCA\Encryption\Tests\Command; + +use OC\Files\View; +use OCA\Encryption\Command\FixEncryptedVersion; +use OCA\Encryption\Util; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\ITempManager; +use OCP\IUserManager; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; +use Test\Traits\EncryptionTrait; +use Test\Traits\MountProviderTrait; +use Test\Traits\UserTrait; + +/** + * Class FixEncryptedVersionTest + * + * @group DB + * @package OCA\Encryption\Tests\Command + */ +class FixEncryptedVersionTest extends TestCase { + use MountProviderTrait; + use EncryptionTrait; + use UserTrait; + + private string $userId; + + private FixEncryptedVersion $fixEncryptedVersion; + + private CommandTester $commandTester; + + protected Util&MockObject $util; + + public function setUp(): void { + parent::setUp(); + + Server::get(IConfig::class)->setAppValue('encryption', 'useMasterKey', '1'); + + $this->util = $this->getMockBuilder(Util::class) + ->disableOriginalConstructor()->getMock(); + + $this->userId = $this->getUniqueId('user_'); + + $this->createUser($this->userId, 'foo12345678'); + $tmpFolder = Server::get(ITempManager::class)->getTemporaryFolder(); + $this->registerMount($this->userId, '\OC\Files\Storage\Local', '/' . $this->userId, ['datadir' => $tmpFolder]); + $this->setupForUser($this->userId, 'foo12345678'); + $this->loginWithEncryption($this->userId); + + $this->fixEncryptedVersion = new FixEncryptedVersion( + Server::get(IConfig::class), + Server::get(LoggerInterface::class), + Server::get(IRootFolder::class), + Server::get(IUserManager::class), + $this->util, + new View('/') + ); + $this->commandTester = new CommandTester($this->fixEncryptedVersion); + + $this->assertTrue(Server::get(\OCP\Encryption\IManager::class)->isEnabled()); + $this->assertTrue(Server::get(\OCP\Encryption\IManager::class)->isReady()); + $this->assertTrue(Server::get(\OCP\Encryption\IManager::class)->isReadyForUser($this->userId)); + } + + /** + * In this test the encrypted version of the file is less than the original value + * but greater than zero + */ + public function testEncryptedVersionLessThanOriginalValue(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + $view->touch('hello.txt'); + $view->touch('world.txt'); + $view->touch('foo.txt'); + $view->file_put_contents('hello.txt', 'a test string for hello'); + $view->file_put_contents('hello.txt', 'Yet another value'); + $view->file_put_contents('hello.txt', 'Lets modify again1'); + $view->file_put_contents('hello.txt', 'Lets modify again2'); + $view->file_put_contents('hello.txt', 'Lets modify again3'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('foo.txt', 'a foo test'); + + $fileInfo1 = $view->getFileInfo('hello.txt'); + + $storage1 = $fileInfo1->getStorage(); + $cache1 = $storage1->getCache(); + $fileCache1 = $cache1->get($fileInfo1->getId()); + + //Now change the encrypted version to two + $cacheInfo = ['encryptedVersion' => 2, 'encrypted' => 2]; + $cache1->put($fileCache1->getPath(), $cacheInfo); + + $fileInfo2 = $view->getFileInfo('world.txt'); + $storage2 = $fileInfo2->getStorage(); + $cache2 = $storage2->getCache(); + $filecache2 = $cache2->get($fileInfo2->getId()); + + //Now change the encrypted version to 1 + $cacheInfo = ['encryptedVersion' => 1, 'encrypted' => 1]; + $cache2->put($filecache2->getPath(), $cacheInfo); + + $this->commandTester->execute([ + 'user' => $this->userId + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/foo.txt\" +The file \"/$this->userId/files/foo.txt\" is: OK", $output); + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/hello.txt\" +Attempting to fix the path: \"/$this->userId/files/hello.txt\" +Decrement the encrypted version to 1 +Increment the encrypted version to 3 +Increment the encrypted version to 4 +Increment the encrypted version to 5 +The file \"/$this->userId/files/hello.txt\" is: OK +Fixed the file: \"/$this->userId/files/hello.txt\" with version 5", $output); + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/world.txt\" +Attempting to fix the path: \"/$this->userId/files/world.txt\" +Increment the encrypted version to 2 +Increment the encrypted version to 3 +Increment the encrypted version to 4 +The file \"/$this->userId/files/world.txt\" is: OK +Fixed the file: \"/$this->userId/files/world.txt\" with version 4", $output); + } + + /** + * In this test the encrypted version of the file is greater than the original value + * but greater than zero + */ + public function testEncryptedVersionGreaterThanOriginalValue(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + $view->touch('hello.txt'); + $view->touch('world.txt'); + $view->touch('foo.txt'); + $view->file_put_contents('hello.txt', 'a test string for hello'); + $view->file_put_contents('hello.txt', 'Lets modify again2'); + $view->file_put_contents('hello.txt', 'Lets modify again3'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('world.txt', 'a test string for world'); + $view->file_put_contents('foo.txt', 'a foo test'); + + $fileInfo1 = $view->getFileInfo('hello.txt'); + + $storage1 = $fileInfo1->getStorage(); + $cache1 = $storage1->getCache(); + $fileCache1 = $cache1->get($fileInfo1->getId()); + + //Now change the encrypted version to fifteen + $cacheInfo = ['encryptedVersion' => 5, 'encrypted' => 5]; + $cache1->put($fileCache1->getPath(), $cacheInfo); + + $fileInfo2 = $view->getFileInfo('world.txt'); + $storage2 = $fileInfo2->getStorage(); + $cache2 = $storage2->getCache(); + $filecache2 = $cache2->get($fileInfo2->getId()); + + //Now change the encrypted version to 1 + $cacheInfo = ['encryptedVersion' => 6, 'encrypted' => 6]; + $cache2->put($filecache2->getPath(), $cacheInfo); + + $this->commandTester->execute([ + 'user' => $this->userId + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/foo.txt\" +The file \"/$this->userId/files/foo.txt\" is: OK", $output); + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/hello.txt\" +Attempting to fix the path: \"/$this->userId/files/hello.txt\" +Decrement the encrypted version to 4 +Decrement the encrypted version to 3 +The file \"/$this->userId/files/hello.txt\" is: OK +Fixed the file: \"/$this->userId/files/hello.txt\" with version 3", $output); + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/world.txt\" +Attempting to fix the path: \"/$this->userId/files/world.txt\" +Decrement the encrypted version to 5 +Decrement the encrypted version to 4 +The file \"/$this->userId/files/world.txt\" is: OK +Fixed the file: \"/$this->userId/files/world.txt\" with version 4", $output); + } + + public function testVersionIsRestoredToOriginalIfNoFixIsFound(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + $view->touch('bar.txt'); + for ($i = 0; $i < 40; $i++) { + $view->file_put_contents('bar.txt', 'a test string for hello ' . $i); + } + + $fileInfo = $view->getFileInfo('bar.txt'); + + $storage = $fileInfo->getStorage(); + $cache = $storage->getCache(); + $fileCache = $cache->get($fileInfo->getId()); + + $cacheInfo = ['encryptedVersion' => 15, 'encrypted' => 15]; + $cache->put($fileCache->getPath(), $cacheInfo); + + $this->commandTester->execute([ + 'user' => $this->userId + ]); + + $cacheInfo = $cache->get($fileInfo->getId()); + $encryptedVersion = $cacheInfo['encryptedVersion']; + + $this->assertEquals(15, $encryptedVersion); + } + + public function testRepairUnencryptedFileWhenVersionIsSet(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + // create a file, it's encrypted and also the version is set in the database + $view->touch('hello.txt'); + + $fileInfo1 = $view->getFileInfo('hello.txt'); + + $storage1 = $fileInfo1->getStorage(); + $cache1 = $storage1->getCache(); + $fileCache1 = $cache1->get($fileInfo1->getId()); + + // Now change the encrypted version + $cacheInfo = ['encryptedVersion' => 1, 'encrypted' => 1]; + $cache1->put($fileCache1->getPath(), $cacheInfo); + + $absPath = $storage1->getSourcePath('') . $fileInfo1->getInternalPath(); + + // create unencrypted file on disk, the version stays + file_put_contents($absPath, 'hello contents'); + + $this->commandTester->execute([ + 'user' => $this->userId + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/hello.txt\" +Attempting to fix the path: \"/$this->userId/files/hello.txt\" +Set the encrypted version to 0 (unencrypted) +The file \"/$this->userId/files/hello.txt\" is: OK +Fixed the file: \"/$this->userId/files/hello.txt\" with version 0 (unencrypted)", $output); + + // the file can be decrypted + $this->assertEquals('hello contents', $view->file_get_contents('hello.txt')); + } + + /** + * Test commands with a file path + */ + public function testExecuteWithFilePathOption(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + $view->touch('hello.txt'); + $view->touch('world.txt'); + + $this->commandTester->execute([ + 'user' => $this->userId, + '--path' => '/hello.txt' + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/hello.txt\" +The file \"/$this->userId/files/hello.txt\" is: OK", $output); + $this->assertStringNotContainsString('world.txt', $output); + } + + /** + * Test commands with a directory path + */ + public function testExecuteWithDirectoryPathOption(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $view = new View('/' . $this->userId . '/files'); + + $view->mkdir('sub'); + $view->touch('sub/hello.txt'); + $view->touch('world.txt'); + + $this->commandTester->execute([ + 'user' => $this->userId, + '--path' => '/sub' + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/sub/hello.txt\" +The file \"/$this->userId/files/sub/hello.txt\" is: OK", $output); + $this->assertStringNotContainsString('world.txt', $output); + } + + public function testExecuteWithNoUser(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $this->commandTester->execute([ + 'user' => null, + '--path' => '/' + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString('Either a user id or --all needs to be provided', $output); + } + + public function testExecuteWithBadUser(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $this->commandTester->execute([ + 'user' => 'nonexisting', + '--path' => '/' + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString('does not exist', $output); + } + + /** + * Test commands with a directory path + */ + public function testExecuteWithNonExistentPath(): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $this->commandTester->execute([ + 'user' => $this->userId, + '--path' => '/non-exist' + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString('Please provide a valid path.', $output); + } + + /** + * Test commands without master key + */ + public function testExecuteWithNoMasterKey(): void { + Server::get(IConfig::class)->setAppValue('encryption', 'useMasterKey', '0'); + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(false); + + $this->commandTester->execute([ + 'user' => $this->userId, + ]); + + $output = $this->commandTester->getDisplay(); + + $this->assertStringContainsString('only works with master key', $output); + } +} diff --git a/apps/encryption/tests/Command/TestEnableMasterKey.php b/apps/encryption/tests/Command/TestEnableMasterKey.php new file mode 100644 index 00000000000..ead3dfd0195 --- /dev/null +++ b/apps/encryption/tests/Command/TestEnableMasterKey.php @@ -0,0 +1,92 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Command; + +use OCA\Encryption\Command\EnableMasterKey; +use OCA\Encryption\Util; +use OCP\IConfig; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class TestEnableMasterKey extends TestCase { + + /** @var EnableMasterKey */ + protected $enableMasterKey; + + /** @var Util | \PHPUnit\Framework\MockObject\MockObject */ + protected $util; + + /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ + protected $config; + + /** @var \Symfony\Component\Console\Helper\QuestionHelper | \PHPUnit\Framework\MockObject\MockObject */ + protected $questionHelper; + + /** @var \Symfony\Component\Console\Output\OutputInterface | \PHPUnit\Framework\MockObject\MockObject */ + protected $output; + + /** @var \Symfony\Component\Console\Input\InputInterface | \PHPUnit\Framework\MockObject\MockObject */ + protected $input; + + protected function setUp(): void { + parent::setUp(); + + $this->util = $this->getMockBuilder(Util::class) + ->disableOriginalConstructor()->getMock(); + $this->config = $this->getMockBuilder(IConfig::class) + ->disableOriginalConstructor()->getMock(); + $this->questionHelper = $this->getMockBuilder(QuestionHelper::class) + ->disableOriginalConstructor()->getMock(); + $this->output = $this->getMockBuilder(OutputInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->input = $this->getMockBuilder(InputInterface::class) + ->disableOriginalConstructor()->getMock(); + + $this->enableMasterKey = new EnableMasterKey($this->util, $this->config, $this->questionHelper); + } + + /** + * + * @param bool $isAlreadyEnabled + * @param string $answer + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestExecute')] + public function testExecute($isAlreadyEnabled, $answer): void { + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn($isAlreadyEnabled); + + if ($isAlreadyEnabled) { + $this->output->expects($this->once())->method('writeln') + ->with('Master key already enabled'); + } else { + if ($answer === 'y') { + $this->questionHelper->expects($this->once())->method('ask')->willReturn(true); + $this->config->expects($this->once())->method('setAppValue') + ->with('encryption', 'useMasterKey', '1'); + } else { + $this->questionHelper->expects($this->once())->method('ask')->willReturn(false); + $this->config->expects($this->never())->method('setAppValue'); + } + } + + $this->invokePrivate($this->enableMasterKey, 'execute', [$this->input, $this->output]); + } + + public static function dataTestExecute() { + return [ + [true, ''], + [false, 'y'], + [false, 'n'], + [false, ''] + ]; + } +} diff --git a/apps/encryption/tests/controller/RecoveryControllerTest.php b/apps/encryption/tests/Controller/RecoveryControllerTest.php index a4349139896..0fec3f4d6a9 100644 --- a/apps/encryption/tests/controller/RecoveryControllerTest.php +++ b/apps/encryption/tests/Controller/RecoveryControllerTest.php @@ -1,76 +1,50 @@ <?php -/** - * @author Clark Tomlinson <fallen013@gmail.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ namespace OCA\Encryption\Tests\Controller; - use OCA\Encryption\Controller\RecoveryController; +use OCA\Encryption\Recovery; use OCP\AppFramework\Http; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class RecoveryControllerTest extends TestCase { - /** - * @var RecoveryController - */ - private $controller; - private $appName; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $configMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $l10nMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $recoveryMock; + protected RecoveryController $controller; - public function adminRecoveryProvider() { + protected IRequest&MockObject $requestMock; + protected IConfig&MockObject $configMock; + protected IL10N&MockObject $l10nMock; + protected Recovery&MockObject $recoveryMock; + + public static function adminRecoveryProvider(): array { return [ - ['test', 'test', '1', 'Recovery key successfully enabled', HTTP::STATUS_OK], - ['', 'test', '1', 'Missing recovery key password', HTTP::STATUS_BAD_REQUEST], - ['test', '', '1', 'Please repeat the recovery key password', HTTP::STATUS_BAD_REQUEST], - ['test', 'soimething that doesn\'t match', '1', 'Repeated recovery key password does not match the provided recovery key password', HTTP::STATUS_BAD_REQUEST], - ['test', 'test', '0', 'Recovery key successfully disabled', HTTP::STATUS_OK], + ['test', 'test', '1', 'Recovery key successfully enabled', Http::STATUS_OK], + ['', 'test', '1', 'Missing recovery key password', Http::STATUS_BAD_REQUEST], + ['test', '', '1', 'Please repeat the recovery key password', Http::STATUS_BAD_REQUEST], + ['test', 'something that doesn\'t match', '1', 'Repeated recovery key password does not match the provided recovery key password', Http::STATUS_BAD_REQUEST], + ['test', 'test', '0', 'Recovery key successfully disabled', Http::STATUS_OK], ]; } /** - * @dataProvider adminRecoveryProvider * @param $recoveryPassword - * @param $passconfirm + * @param $passConfirm * @param $enableRecovery * @param $expectedMessage * @param $expectedStatus */ - public function testAdminRecovery($recoveryPassword, $passconfirm, $enableRecovery, $expectedMessage, $expectedStatus) { - - + #[\PHPUnit\Framework\Attributes\DataProvider('adminRecoveryProvider')] + public function testAdminRecovery($recoveryPassword, $passConfirm, $enableRecovery, $expectedMessage, $expectedStatus): void { $this->recoveryMock->expects($this->any()) ->method('enableAdminRecovery') ->willReturn(true); @@ -80,42 +54,40 @@ class RecoveryControllerTest extends TestCase { ->willReturn(true); $response = $this->controller->adminRecovery($recoveryPassword, - $passconfirm, + $passConfirm, $enableRecovery); $this->assertEquals($expectedMessage, $response->getData()['data']['message']); $this->assertEquals($expectedStatus, $response->getStatus()); - - } - public function changeRecoveryPasswordProvider() { + public static function changeRecoveryPasswordProvider(): array { return [ - ['test', 'test', 'oldtestFail', 'Could not change the password. Maybe the old password was not correct.', HTTP::STATUS_BAD_REQUEST], - ['test', 'test', 'oldtest', 'Password successfully changed.', HTTP::STATUS_OK], - ['test', 'notmatch', 'oldtest', 'Repeated recovery key password does not match the provided recovery key password', HTTP::STATUS_BAD_REQUEST], - ['', 'test', 'oldtest', 'Please provide a new recovery password', HTTP::STATUS_BAD_REQUEST], - ['test', 'test', '', 'Please provide the old recovery password', HTTP::STATUS_BAD_REQUEST] + ['test', 'test', 'oldtestFail', 'Could not change the password. Maybe the old password was not correct.', Http::STATUS_BAD_REQUEST], + ['test', 'test', 'oldtest', 'Password successfully changed.', Http::STATUS_OK], + ['test', 'notmatch', 'oldtest', 'Repeated recovery key password does not match the provided recovery key password', Http::STATUS_BAD_REQUEST], + ['', 'test', 'oldtest', 'Please provide a new recovery password', Http::STATUS_BAD_REQUEST], + ['test', 'test', '', 'Please provide the old recovery password', Http::STATUS_BAD_REQUEST] ]; } /** - * @dataProvider changeRecoveryPasswordProvider * @param $password * @param $confirmPassword * @param $oldPassword * @param $expectedMessage * @param $expectedStatus */ - public function testChangeRecoveryPassword($password, $confirmPassword, $oldPassword, $expectedMessage, $expectedStatus) { + #[\PHPUnit\Framework\Attributes\DataProvider('changeRecoveryPasswordProvider')] + public function testChangeRecoveryPassword($password, $confirmPassword, $oldPassword, $expectedMessage, $expectedStatus): void { $this->recoveryMock->expects($this->any()) ->method('changeRecoveryKeyPassword') ->with($password, $oldPassword) - ->will($this->returnValueMap([ + ->willReturnMap([ ['test', 'oldTestFail', false], ['test', 'oldtest', true] - ])); + ]); $response = $this->controller->changeRecoveryPassword($password, $oldPassword, @@ -123,11 +95,9 @@ class RecoveryControllerTest extends TestCase { $this->assertEquals($expectedMessage, $response->getData()['data']['message']); $this->assertEquals($expectedStatus, $response->getStatus()); - - } - public function userSetRecoveryProvider() { + public static function userSetRecoveryProvider(): array { return [ ['1', 'Recovery Key enabled', Http::STATUS_OK], ['0', 'Could not enable the recovery key, please try again or contact your administrator', Http::STATUS_BAD_REQUEST] @@ -135,41 +105,39 @@ class RecoveryControllerTest extends TestCase { } /** - * @dataProvider userSetRecoveryProvider * @param $enableRecovery * @param $expectedMessage * @param $expectedStatus */ - public function testUserSetRecovery($enableRecovery, $expectedMessage, $expectedStatus) { + #[\PHPUnit\Framework\Attributes\DataProvider('userSetRecoveryProvider')] + public function testUserSetRecovery($enableRecovery, $expectedMessage, $expectedStatus): void { $this->recoveryMock->expects($this->any()) ->method('setRecoveryForUser') ->with($enableRecovery) - ->will($this->returnValueMap([ + ->willReturnMap([ ['1', true], ['0', false] - ])); + ]); $response = $this->controller->userSetRecovery($enableRecovery); $this->assertEquals($expectedMessage, $response->getData()['data']['message']); $this->assertEquals($expectedStatus, $response->getStatus()); - } - protected function setUp() { + protected function setUp(): void { parent::setUp(); - $this->appName = 'encryption'; - $this->requestMock = $this->getMockBuilder('\OCP\IRequest') + $this->requestMock = $this->getMockBuilder(IRequest::class) ->disableOriginalConstructor() ->getMock(); - $this->configMock = $this->getMockBuilder('OCP\IConfig') + $this->configMock = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); - $this->l10nMock = $this->getMockBuilder('OCP\IL10N') + $this->l10nMock = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor() ->getMock(); @@ -178,15 +146,14 @@ class RecoveryControllerTest extends TestCase { ->method('t') ->willReturnArgument(0); - $this->recoveryMock = $this->getMockBuilder('OCA\Encryption\Recovery') + $this->recoveryMock = $this->getMockBuilder(Recovery::class) ->disableOriginalConstructor() ->getMock(); - $this->controller = new RecoveryController($this->appName, + $this->controller = new RecoveryController('encryption', $this->requestMock, $this->configMock, $this->l10nMock, $this->recoveryMock); } - } diff --git a/apps/encryption/tests/controller/SettingsControllerTest.php b/apps/encryption/tests/Controller/SettingsControllerTest.php index 61391a33002..bee20f67cec 100644 --- a/apps/encryption/tests/controller/SettingsControllerTest.php +++ b/apps/encryption/tests/Controller/SettingsControllerTest.php @@ -1,115 +1,83 @@ <?php + +declare(strict_types=1); + /** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Encryption\Tests\Controller; use OCA\Encryption\Controller\SettingsController; +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; use OCA\Encryption\Session; +use OCA\Encryption\Util; use OCP\AppFramework\Http; +use OCP\IL10N; +use OCP\IRequest; +use OCP\ISession; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SettingsControllerTest extends TestCase { - /** @var SettingsController */ - private $controller; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $requestMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $l10nMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $userManagerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $userSessionMock; + protected SettingsController $controller; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $keyManagerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $cryptMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $sessionMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $ocSessionMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $utilMock; - - protected function setUp() { + protected IRequest&MockObject $requestMock; + protected IL10N&MockObject $l10nMock; + protected IUserManager&MockObject $userManagerMock; + protected IUserSession&MockObject $userSessionMock; + protected KeyManager&MockObject $keyManagerMock; + protected Crypt&MockObject $cryptMock; + protected Session&MockObject $sessionMock; + protected IUser&MockObject $user; + protected ISession&MockObject $ocSessionMock; + protected Util&MockObject $utilMock; + protected function setUp(): void { parent::setUp(); - $this->requestMock = $this->getMock('OCP\IRequest'); + $this->requestMock = $this->createMock(IRequest::class); - $this->l10nMock = $this->getMockBuilder('OCP\IL10N') + $this->l10nMock = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor()->getMock(); $this->l10nMock->expects($this->any()) ->method('t') - ->will($this->returnCallback(function($message) { + ->willReturnCallback(function ($message) { return $message; - })); + }); - $this->userManagerMock = $this->getMockBuilder('OCP\IUserManager') + $this->userManagerMock = $this->getMockBuilder(IUserManager::class) ->disableOriginalConstructor()->getMock(); - $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') + $this->keyManagerMock = $this->getMockBuilder(KeyManager::class) ->disableOriginalConstructor()->getMock(); - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') + $this->cryptMock = $this->getMockBuilder(Crypt::class) ->disableOriginalConstructor()->getMock(); - $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'getUID', - 'login', - 'logout', - 'setUser', - 'getUser', - 'canChangePassword', - ]) - ->getMock(); - - $this->ocSessionMock = $this->getMockBuilder('\OCP\ISession')->disableOriginalConstructor()->getMock(); + $this->ocSessionMock = $this->getMockBuilder(ISession::class)->disableOriginalConstructor()->getMock(); - $this->userSessionMock->expects($this->any()) + $this->user = $this->createMock(IUser::class); + $this->user->expects($this->any()) ->method('getUID') ->willReturn('testUserUid'); + $this->userSessionMock = $this->createMock(IUserSession::class); $this->userSessionMock->expects($this->any()) - ->method($this->anything()) - ->will($this->returnSelf()); + ->method('getUser') + ->willReturn($this->user); - $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor()->getMock(); - $this->utilMock = $this->getMockBuilder('OCA\Encryption\Util') + $this->utilMock = $this->getMockBuilder(Util::class) ->disableOriginalConstructor() ->getMock(); @@ -130,12 +98,13 @@ class SettingsControllerTest extends TestCase { /** * test updatePrivateKeyPassword() if wrong new password was entered */ - public function testUpdatePrivateKeyPasswordWrongNewPassword() { - + public function testUpdatePrivateKeyPasswordWrongNewPassword(): void { $oldPassword = 'old'; $newPassword = 'new'; - $this->userSessionMock->expects($this->once())->method('getUID')->willReturn('uid'); + $this->user->expects($this->any()) + ->method('getUID') + ->willReturn('uid'); $this->userManagerMock ->expects($this->exactly(2)) @@ -154,8 +123,7 @@ class SettingsControllerTest extends TestCase { /** * test updatePrivateKeyPassword() if wrong old password was entered */ - public function testUpdatePrivateKeyPasswordWrongOldPassword() { - + public function testUpdatePrivateKeyPasswordWrongOldPassword(): void { $oldPassword = 'old'; $newPassword = 'new'; @@ -181,26 +149,22 @@ class SettingsControllerTest extends TestCase { /** * test updatePrivateKeyPassword() with the correct old and new password */ - public function testUpdatePrivateKeyPassword() { - + public function testUpdatePrivateKeyPassword(): void { $oldPassword = 'old'; $newPassword = 'new'; $this->ocSessionMock->expects($this->once()) - ->method('get')->with('loginname')->willReturn('testUser'); + ->method('get') + ->with('loginname') + ->willReturn('testUser'); $this->userManagerMock - ->expects($this->at(0)) - ->method('checkPassword') - ->with('testUserUid', 'new') - ->willReturn(false); - $this->userManagerMock - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('checkPassword') - ->with('testUser', 'new') - ->willReturn(true); - - + ->willReturnMap([ + ['testUserUid', 'new', false], + ['testUser', 'new', true], + ]); $this->cryptMock ->expects($this->once()) @@ -242,10 +206,9 @@ class SettingsControllerTest extends TestCase { $data['message']); } - function testSetEncryptHomeStorage() { + public function testSetEncryptHomeStorage(): void { $value = true; $this->utilMock->expects($this->once())->method('setEncryptHomeStorage')->with($value); $this->controller->setEncryptHomeStorage($value); } - } diff --git a/apps/encryption/tests/Controller/StatusControllerTest.php b/apps/encryption/tests/Controller/StatusControllerTest.php new file mode 100644 index 00000000000..1bbcad77411 --- /dev/null +++ b/apps/encryption/tests/Controller/StatusControllerTest.php @@ -0,0 +1,74 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Controller; + +use OCA\Encryption\Controller\StatusController; +use OCA\Encryption\Session; +use OCP\Encryption\IManager; +use OCP\IL10N; +use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class StatusControllerTest extends TestCase { + + protected IRequest&MockObject $requestMock; + protected IL10N&MockObject $l10nMock; + protected Session&MockObject $sessionMock; + protected IManager&MockObject $encryptionManagerMock; + + protected StatusController $controller; + + protected function setUp(): void { + parent::setUp(); + + $this->sessionMock = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor()->getMock(); + $this->requestMock = $this->createMock(IRequest::class); + + $this->l10nMock = $this->getMockBuilder(IL10N::class) + ->disableOriginalConstructor()->getMock(); + $this->l10nMock->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message) { + return $message; + }); + $this->encryptionManagerMock = $this->createMock(IManager::class); + + $this->controller = new StatusController('encryptionTest', + $this->requestMock, + $this->l10nMock, + $this->sessionMock, + $this->encryptionManagerMock); + } + + /** + * + * @param string $status + * @param string $expectedStatus + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetStatus')] + public function testGetStatus($status, $expectedStatus): void { + $this->sessionMock->expects($this->once()) + ->method('getStatus')->willReturn($status); + $result = $this->controller->getStatus(); + $data = $result->getData(); + $this->assertSame($expectedStatus, $data['status']); + } + + public static function dataTestGetStatus(): array { + return [ + [Session::INIT_EXECUTED, 'interactionNeeded'], + [Session::INIT_SUCCESSFUL, 'success'], + [Session::NOT_INITIALIZED, 'interactionNeeded'], + ['unknown', 'error'], + ]; + } +} diff --git a/apps/encryption/tests/lib/crypto/cryptTest.php b/apps/encryption/tests/Crypto/CryptTest.php index 2f290db21a1..1355e2c855d 100644 --- a/apps/encryption/tests/lib/crypto/cryptTest.php +++ b/apps/encryption/tests/Crypto/CryptTest.php @@ -1,68 +1,46 @@ <?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Lukas Reschke <lukas@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ +declare(strict_types=1); -namespace OCA\Encryption\Tests\lib\Crypto; - +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Crypto; use OCA\Encryption\Crypto\Crypt; +use OCP\Encryption\Exceptions\GenericEncryptionException; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Test\TestCase; -class cryptTest extends TestCase { - - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $logger; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $userSession; +class CryptTest extends TestCase { + protected LoggerInterface&MockObject $logger; + protected IUserSession&MockObject $userSession; + protected IConfig&MockObject $config; + protected IL10N&MockObject $l; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $config; + protected Crypt $crypt; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $l; - - /** @var Crypt */ - private $crypt; - - public function setUp() { + protected function setUp(): void { parent::setUp(); - $this->logger = $this->getMockBuilder('OCP\ILogger') + $this->logger = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMock(); $this->logger->expects($this->any()) - ->method('warning') - ->willReturn(true); - $this->userSession = $this->getMockBuilder('OCP\IUserSession') + ->method('warning'); + $this->userSession = $this->getMockBuilder(IUserSession::class) ->disableOriginalConstructor() ->getMock(); - $this->config = $this->getMockBuilder('OCP\IConfig') + $this->config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); - $this->l = $this->getMock('OCP\IL10N'); + $this->l = $this->createMock(IL10N::class); $this->crypt = new Crypt($this->logger, $this->userSession, $this->config, $this->l); } @@ -70,12 +48,11 @@ class cryptTest extends TestCase { /** * test getOpenSSLConfig without any additional parameters */ - public function testGetOpenSSLConfigBasic() { - + public function testGetOpenSSLConfigBasic(): void { $this->config->expects($this->once()) ->method('getSystemValue') ->with($this->equalTo('openssl'), $this->equalTo([])) - ->willReturn(array()); + ->willReturn([]); $result = self::invokePrivate($this->crypt, 'getOpenSSLConfig'); $this->assertSame(1, count($result)); @@ -86,12 +63,11 @@ class cryptTest extends TestCase { /** * test getOpenSSLConfig with additional parameters defined in config.php */ - public function testGetOpenSSLConfig() { - + public function testGetOpenSSLConfig(): void { $this->config->expects($this->once()) ->method('getSystemValue') ->with($this->equalTo('openssl'), $this->equalTo([])) - ->willReturn(array('foo' => 'bar', 'private_key_bits' => 1028)); + ->willReturn(['foo' => 'bar', 'private_key_bits' => 1028]); $result = self::invokePrivate($this->crypt, 'getOpenSSLConfig'); $this->assertSame(2, count($result)); @@ -104,13 +80,11 @@ class cryptTest extends TestCase { /** * test generateHeader with valid key formats - * - * @dataProvider dataTestGenerateHeader */ - public function testGenerateHeader($keyFormat, $expected) { - + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGenerateHeader')] + public function testGenerateHeader($keyFormat, $expected): void { $this->config->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueString') ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR')) ->willReturn('AES-128-CFB'); @@ -126,89 +100,84 @@ class cryptTest extends TestCase { /** * test generateHeader with invalid key format * - * @expectedException \InvalidArgumentException */ - public function testGenerateHeaderInvalid() { + public function testGenerateHeaderInvalid(): void { + $this->expectException(\InvalidArgumentException::class); + $this->crypt->generateHeader('unknown'); } - /** - * @return array - */ - public function dataTestGenerateHeader() { + public static function dataTestGenerateHeader(): array { return [ - [null, 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash:HEND'], - ['password', 'HBEGIN:cipher:AES-128-CFB:keyFormat:password:HEND'], - ['hash', 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash:HEND'] + [null, 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash2:encoding:binary:HEND'], + ['password', 'HBEGIN:cipher:AES-128-CFB:keyFormat:password:encoding:binary:HEND'], + ['hash', 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash:encoding:binary:HEND'] ]; } - public function testGetCipherWithInvalidCipher() { + public function testGetCipherWithInvalidCipher(): void { $this->config->expects($this->once()) - ->method('getSystemValue') - ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR')) - ->willReturn('Not-Existing-Cipher'); + ->method('getSystemValueString') + ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR')) + ->willReturn('Not-Existing-Cipher'); $this->logger ->expects($this->once()) ->method('warning') ->with('Unsupported cipher (Not-Existing-Cipher) defined in config.php supported. Falling back to AES-256-CTR'); - $this->assertSame('AES-256-CTR', $this->crypt->getCipher()); + $this->assertSame('AES-256-CTR', $this->crypt->getCipher()); } /** - * @dataProvider dataProviderGetCipher * @param string $configValue * @param string $expected */ - public function testGetCipher($configValue, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderGetCipher')] + public function testGetCipher($configValue, $expected): void { $this->config->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueString') ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR')) ->willReturn($configValue); $this->assertSame($expected, $this->crypt->getCipher() ); - } /** * data provider for testGetCipher - * - * @return array */ - public function dataProviderGetCipher() { - return array( - array('AES-128-CFB', 'AES-128-CFB'), - array('AES-256-CFB', 'AES-256-CFB'), - array('AES-128-CTR', 'AES-128-CTR'), - array('AES-256-CTR', 'AES-256-CTR'), - - array('unknown', 'AES-256-CTR') - ); + public static function dataProviderGetCipher(): array { + return [ + ['AES-128-CFB', 'AES-128-CFB'], + ['AES-256-CFB', 'AES-256-CFB'], + ['AES-128-CTR', 'AES-128-CTR'], + ['AES-256-CTR', 'AES-256-CTR'], + + ['unknown', 'AES-256-CTR'] + ]; } /** * test concatIV() */ - public function testConcatIV() { - + public function testConcatIV(): void { $result = self::invokePrivate( $this->crypt, 'concatIV', - array('content', 'my_iv')); + ['content', 'my_iv']); $this->assertSame('content00iv00my_iv', $result ); } - /** - * @dataProvider dataTestSplitMetaData - */ - public function testSplitMetaData($data, $expected) { - $result = self::invokePrivate($this->crypt, 'splitMetaData', array($data, 'AES-256-CFB')); + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitMetaData')] + public function testSplitMetaData($data, $expected): void { + $this->config->method('getSystemValueBool') + ->with('encryption_skip_signature_check', false) + ->willReturn(true); + $result = self::invokePrivate($this->crypt, 'splitMetaData', [$data, 'AES-256-CFB']); $this->assertTrue(is_array($result)); $this->assertSame(3, count($result)); $this->assertArrayHasKey('encrypted', $result); @@ -219,7 +188,7 @@ class cryptTest extends TestCase { $this->assertSame($expected['signature'], $result['signature']); } - public function dataTestSplitMetaData() { + public static function dataTestSplitMetaData(): array { return [ ['encryptedContent00iv001234567890123456xx', ['encrypted' => 'encryptedContent', 'iv' => '1234567890123456', 'signature' => false]], @@ -228,32 +197,32 @@ class cryptTest extends TestCase { ]; } - /** - * @dataProvider dataTestHasSignature - */ - public function testHasSignature($data, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestHasSignature')] + public function testHasSignature($data, $expected): void { + $this->config->method('getSystemValueBool') + ->with('encryption_skip_signature_check', false) + ->willReturn(true); $this->assertSame($expected, - $this->invokePrivate($this->crypt, 'hasSignature', array($data, 'AES-256-CFB')) + $this->invokePrivate($this->crypt, 'hasSignature', [$data, 'AES-256-CFB']) ); } - public function dataTestHasSignature() { + public static function dataTestHasSignature(): array { return [ ['encryptedContent00iv001234567890123456xx', false], ['encryptedContent00iv00123456789012345600sig00e1992521e437f6915f9173b190a512cfc38a00ac24502db44e0ba10c2bb0cc86xxx', true] ]; } - /** - * @dataProvider dataTestHasSignatureFail - * @expectedException \OC\HintException - */ - public function testHasSignatureFail($cipher) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestHasSignatureFail')] + public function testHasSignatureFail($cipher): void { + $this->expectException(GenericEncryptionException::class); + $data = 'encryptedContent00iv001234567890123456xx'; - $this->invokePrivate($this->crypt, 'hasSignature', array($data, $cipher)); + $this->invokePrivate($this->crypt, 'hasSignature', [$data, $cipher]); } - public function dataTestHasSignatureFail() { + public static function dataTestHasSignatureFail(): array { return [ ['AES-256-CTR'], ['aes-256-ctr'], @@ -265,49 +234,48 @@ class cryptTest extends TestCase { /** * test addPadding() */ - public function testAddPadding() { - $result = self::invokePrivate($this->crypt, 'addPadding', array('data')); + public function testAddPadding(): void { + $result = self::invokePrivate($this->crypt, 'addPadding', ['data']); $this->assertSame('dataxxx', $result); } /** * test removePadding() * - * @dataProvider dataProviderRemovePadding * @param $data * @param $expected */ - public function testRemovePadding($data, $expected) { - $result = self::invokePrivate($this->crypt, 'removePadding', array($data)); + #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderRemovePadding')] + public function testRemovePadding($data, $expected): void { + $result = self::invokePrivate($this->crypt, 'removePadding', [$data]); $this->assertSame($expected, $result); } /** * data provider for testRemovePadding - * - * @return array */ - public function dataProviderRemovePadding() { - return array( - array('dataxx', 'data'), - array('data', false) - ); + public static function dataProviderRemovePadding(): array { + return [ + ['dataxx', 'data'], + ['data', false] + ]; } /** * test parseHeader() */ - public function testParseHeader() { - - $header= 'HBEGIN:foo:bar:cipher:AES-256-CFB:HEND'; - $result = self::invokePrivate($this->crypt, 'parseHeader', array($header)); + public function testParseHeader(): void { + $header = 'HBEGIN:foo:bar:cipher:AES-256-CFB:encoding:binary:HEND'; + $result = self::invokePrivate($this->crypt, 'parseHeader', [$header]); $this->assertTrue(is_array($result)); - $this->assertSame(2, count($result)); + $this->assertSame(3, count($result)); $this->assertArrayHasKey('foo', $result); $this->assertArrayHasKey('cipher', $result); + $this->assertArrayHasKey('encoding', $result); $this->assertSame('bar', $result['foo']); $this->assertSame('AES-256-CFB', $result['cipher']); + $this->assertSame('binary', $result['encoding']); } /** @@ -316,24 +284,24 @@ class cryptTest extends TestCase { * @return string */ public function testEncrypt() { - $decrypted = 'content'; $password = 'password'; + $cipher = 'AES-256-CTR'; $iv = self::invokePrivate($this->crypt, 'generateIv'); $this->assertTrue(is_string($iv)); $this->assertSame(16, strlen($iv)); - $result = self::invokePrivate($this->crypt, 'encrypt', array($decrypted, $iv, $password)); + $result = self::invokePrivate($this->crypt, 'encrypt', [$decrypted, $iv, $password, $cipher]); $this->assertTrue(is_string($result)); - return array( + return [ 'password' => $password, 'iv' => $iv, + 'cipher' => $cipher, 'encrypted' => $result, - 'decrypted' => $decrypted); - + 'decrypted' => $decrypted]; } /** @@ -341,23 +309,20 @@ class cryptTest extends TestCase { * * @depends testEncrypt */ - public function testDecrypt($data) { - + public function testDecrypt($data): void { $result = self::invokePrivate( $this->crypt, 'decrypt', - array($data['encrypted'], $data['iv'], $data['password'])); + [$data['encrypted'], $data['iv'], $data['password'], $data['cipher'], true]); $this->assertSame($data['decrypted'], $result); - } /** * test return values of valid ciphers - * - * @dataProvider dataTestGetKeySize */ - public function testGetKeySize($cipher, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetKeySize')] + public function testGetKeySize($cipher, $expected): void { $result = $this->invokePrivate($this->crypt, 'getKeySize', [$cipher]); $this->assertSame($expected, $result); } @@ -365,16 +330,14 @@ class cryptTest extends TestCase { /** * test exception if cipher is unknown * - * @expectedException \InvalidArgumentException */ - public function testGetKeySizeFailure() { + public function testGetKeySizeFailure(): void { + $this->expectException(\InvalidArgumentException::class); + $this->invokePrivate($this->crypt, 'getKeySize', ['foo']); } - /** - * @return array - */ - public function dataTestGetKeySize() { + public static function dataTestGetKeySize(): array { return [ ['AES-256-CFB', 32], ['AES-128-CFB', 16], @@ -383,28 +346,28 @@ class cryptTest extends TestCase { ]; } - /** - * @dataProvider dataTestDecryptPrivateKey - */ - public function testDecryptPrivateKey($header, $privateKey, $expectedCipher, $isValidKey, $expected) { - /** @var \OCA\Encryption\Crypto\Crypt | \PHPUnit_Framework_MockObject_MockObject $crypt */ - $crypt = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') - ->setConstructorArgs( - [ - $this->logger, - $this->userSession, - $this->config, - $this->l - ] - ) - ->setMethods( - [ - 'parseHeader', - 'generatePasswordHash', - 'symmetricDecryptFileContent', - 'isValidPrivateKey' - ] - ) + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDecryptPrivateKey')] + public function testDecryptPrivateKey($header, $privateKey, $expectedCipher, $isValidKey, $expected): void { + $this->config->method('getSystemValueBool') + ->willReturnMap([ + ['encryption.legacy_format_support', false, true], + ['encryption.use_legacy_base64_encoding', false, false], + ]); + + /** @var Crypt|\PHPUnit\Framework\MockObject\MockObject $crypt */ + $crypt = $this->getMockBuilder(Crypt::class) + ->setConstructorArgs([ + $this->logger, + $this->userSession, + $this->config, + $this->l + ]) + ->onlyMethods([ + 'parseHeader', + 'generatePasswordHash', + 'symmetricDecryptFileContent', + 'isValidPrivateKey' + ]) ->getMock(); $crypt->expects($this->once())->method('parseHeader')->willReturn($header); @@ -425,10 +388,7 @@ class cryptTest extends TestCase { $this->assertSame($expected, $result); } - /** - * @return array - */ - public function dataTestDecryptPrivateKey() { + public static function dataTestDecryptPrivateKey(): array { return [ [['cipher' => 'AES-128-CFB', 'keyFormat' => 'password'], 'HBEGIN:HENDprivateKey', 'AES-128-CFB', true, 'key'], [['cipher' => 'AES-256-CFB', 'keyFormat' => 'password'], 'HBEGIN:HENDprivateKey', 'AES-256-CFB', true, 'key'], @@ -439,7 +399,7 @@ class cryptTest extends TestCase { ]; } - public function testIsValidPrivateKey() { + public function testIsValidPrivateKey(): void { $res = openssl_pkey_new(); openssl_pkey_export($res, $privateKey); @@ -454,4 +414,16 @@ class cryptTest extends TestCase { ); } + public function testMultiKeyEncrypt(): void { + $res = openssl_pkey_new(); + openssl_pkey_export($res, $privateKey); + $publicKeyPem = openssl_pkey_get_details($res)['key']; + $publicKey = openssl_pkey_get_public($publicKeyPem); + + $shareKeys = $this->crypt->multiKeyEncrypt('content', ['user1' => $publicKey]); + $this->assertEquals( + 'content', + $this->crypt->multiKeyDecrypt($shareKeys['user1'], $privateKey) + ); + } } diff --git a/apps/encryption/tests/lib/crypto/decryptalltest.php b/apps/encryption/tests/Crypto/DecryptAllTest.php index 0945692e427..82e6100bce5 100644 --- a/apps/encryption/tests/lib/crypto/decryptalltest.php +++ b/apps/encryption/tests/Crypto/DecryptAllTest.php @@ -1,68 +1,45 @@ <?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Encryption\Tests\lib\Crypto; +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Crypto; use OCA\Encryption\Crypto\Crypt; use OCA\Encryption\Crypto\DecryptAll; use OCA\Encryption\KeyManager; use OCA\Encryption\Session; use OCA\Encryption\Util; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Console\Helper\QuestionHelper; use Test\TestCase; class DecryptAllTest extends TestCase { - /** @var DecryptAll */ - protected $instance; - - /** @var Util | \PHPUnit_Framework_MockObject_MockObject */ - protected $util; + protected DecryptAll $instance; - /** @var KeyManager | \PHPUnit_Framework_MockObject_MockObject */ - protected $keyManager; + protected Util&MockObject $util; + protected KeyManager&MockObject $keyManager; + protected Crypt&MockObject $crypt; + protected Session&MockObject $session; + protected QuestionHelper&MockObject $questionHelper; - /** @var Crypt | \PHPUnit_Framework_MockObject_MockObject */ - protected $crypt; - - /** @var Session | \PHPUnit_Framework_MockObject_MockObject */ - protected $session; - - /** @var QuestionHelper | \PHPUnit_Framework_MockObject_MockObject */ - protected $questionHelper; - - public function setUp() { + protected function setUp(): void { parent::setUp(); - $this->util = $this->getMockBuilder('OCA\Encryption\Util') + $this->util = $this->getMockBuilder(Util::class) ->disableOriginalConstructor()->getMock(); - $this->keyManager = $this->getMockBuilder('OCA\Encryption\KeyManager') + $this->keyManager = $this->getMockBuilder(KeyManager::class) ->disableOriginalConstructor()->getMock(); - $this->crypt = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') + $this->crypt = $this->getMockBuilder(Crypt::class) ->disableOriginalConstructor()->getMock(); - $this->session = $this->getMockBuilder('OCA\Encryption\Session') + $this->session = $this->getMockBuilder(Session::class) ->disableOriginalConstructor()->getMock(); - $this->questionHelper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + $this->questionHelper = $this->getMockBuilder(QuestionHelper::class) ->disableOriginalConstructor()->getMock(); $this->instance = new DecryptAll( @@ -74,7 +51,7 @@ class DecryptAllTest extends TestCase { ); } - public function testUpdateSession() { + public function testUpdateSession(): void { $this->session->expects($this->once())->method('prepareDecryptAll') ->with('user1', 'key1'); @@ -82,15 +59,16 @@ class DecryptAllTest extends TestCase { } /** - * @dataProvider dataTestGetPrivateKey * * @param string $user * @param string $recoveryKeyId */ - public function testGetPrivateKey($user, $recoveryKeyId, $masterKeyId) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetPrivateKey')] + public function testGetPrivateKey($user, $recoveryKeyId, $masterKeyId): void { $password = 'passwd'; $recoveryKey = 'recoveryKey'; $userKey = 'userKey'; + $masterKey = 'userKey'; $unencryptedKey = 'unencryptedKey'; $this->keyManager->expects($this->any())->method('getRecoveryKeyId') @@ -108,7 +86,6 @@ class DecryptAllTest extends TestCase { $this->keyManager->expects($this->never())->method('getPrivateKey'); $this->crypt->expects($this->once())->method('decryptPrivateKey') ->with($masterKey, $password, $masterKeyId)->willReturn($unencryptedKey); - } else { $this->keyManager->expects($this->never())->method('getSystemPrivateKey'); $this->keyManager->expects($this->once())->method('getPrivateKey') @@ -122,12 +99,11 @@ class DecryptAllTest extends TestCase { ); } - public function dataTestGetPrivateKey() { + public static function dataTestGetPrivateKey() { return [ ['user1', 'recoveryKey', 'masterKeyId'], ['recoveryKeyId', 'recoveryKeyId', 'masterKeyId'], ['masterKeyId', 'masterKeyId', 'masterKeyId'] ]; } - } diff --git a/apps/encryption/tests/Crypto/EncryptAllTest.php b/apps/encryption/tests/Crypto/EncryptAllTest.php new file mode 100644 index 00000000000..c56e3375a73 --- /dev/null +++ b/apps/encryption/tests/Crypto/EncryptAllTest.php @@ -0,0 +1,385 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Crypto; + +use OC\Files\View; +use OCA\Encryption\Crypto\EncryptAll; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Users\Setup; +use OCA\Encryption\Util; +use OCP\Files\FileInfo; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IUserManager; +use OCP\L10N\IFactory; +use OCP\Mail\IMailer; +use OCP\Security\ISecureRandom; +use OCP\UserInterface; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class EncryptAllTest extends TestCase { + + protected KeyManager&MockObject $keyManager; + protected Util&MockObject $util; + protected IUserManager&MockObject $userManager; + protected Setup&MockObject $setupUser; + protected View&MockObject $view; + protected IConfig&MockObject $config; + protected IMailer&MockObject $mailer; + protected IL10N&MockObject $l; + protected IFactory&MockObject $l10nFactory; + protected \Symfony\Component\Console\Helper\QuestionHelper&MockObject $questionHelper; + protected \Symfony\Component\Console\Input\InputInterface&MockObject $inputInterface; + protected \Symfony\Component\Console\Output\OutputInterface&MockObject $outputInterface; + protected UserInterface&MockObject $userInterface; + protected ISecureRandom&MockObject $secureRandom; + protected LoggerInterface&MockObject $logger; + + protected EncryptAll $encryptAll; + + protected function setUp(): void { + parent::setUp(); + $this->setupUser = $this->getMockBuilder(Setup::class) + ->disableOriginalConstructor()->getMock(); + $this->keyManager = $this->getMockBuilder(KeyManager::class) + ->disableOriginalConstructor()->getMock(); + $this->util = $this->getMockBuilder(Util::class) + ->disableOriginalConstructor()->getMock(); + $this->userManager = $this->getMockBuilder(IUserManager::class) + ->disableOriginalConstructor()->getMock(); + $this->view = $this->getMockBuilder(View::class) + ->disableOriginalConstructor()->getMock(); + $this->config = $this->getMockBuilder(IConfig::class) + ->disableOriginalConstructor()->getMock(); + $this->mailer = $this->getMockBuilder(IMailer::class) + ->disableOriginalConstructor()->getMock(); + $this->l10nFactory = $this->createMock(IFactory::class); + $this->l = $this->getMockBuilder(IL10N::class) + ->disableOriginalConstructor()->getMock(); + $this->questionHelper = $this->getMockBuilder(QuestionHelper::class) + ->disableOriginalConstructor()->getMock(); + $this->inputInterface = $this->getMockBuilder(InputInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->outputInterface = $this->getMockBuilder(OutputInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->userInterface = $this->getMockBuilder(UserInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->logger = $this->createMock(LoggerInterface::class); + + /** + * We need format method to return a string + * @var OutputFormatterInterface&MockObject + */ + $outputFormatter = $this->createMock(OutputFormatterInterface::class); + $outputFormatter->method('isDecorated')->willReturn(false); + $outputFormatter->method('format')->willReturnArgument(0); + + $this->outputInterface->expects($this->any())->method('getFormatter') + ->willReturn($outputFormatter); + + $this->userManager->expects($this->any())->method('getBackends')->willReturn([$this->userInterface]); + $this->userInterface->expects($this->any())->method('getUsers')->willReturn(['user1', 'user2']); + + $this->secureRandom = $this->getMockBuilder(ISecureRandom::class)->disableOriginalConstructor()->getMock(); + $this->secureRandom->expects($this->any())->method('generate')->willReturn('12345678'); + + + $this->encryptAll = new EncryptAll( + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ); + } + + protected function createFileInfoMock($type, string $name): FileInfo&MockObject { + $fileInfo = $this->createMock(FileInfo::class); + $fileInfo->method('getType')->willReturn($type); + $fileInfo->method('getName')->willReturn($name); + return $fileInfo; + } + + public function testEncryptAll(): void { + /** @var EncryptAll&MockObject $encryptAll */ + $encryptAll = $this->getMockBuilder(EncryptAll::class) + ->setConstructorArgs( + [ + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ] + ) + ->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords']) + ->getMock(); + + $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false); + $encryptAll->expects($this->once())->method('createKeyPairs')->with(); + $encryptAll->expects($this->once())->method('outputPasswords')->with(); + $encryptAll->expects($this->once())->method('encryptAllUsersFiles')->with(); + + $encryptAll->encryptAll($this->inputInterface, $this->outputInterface); + } + + public function testEncryptAllWithMasterKey(): void { + /** @var EncryptAll&MockObject $encryptAll */ + $encryptAll = $this->getMockBuilder(EncryptAll::class) + ->setConstructorArgs( + [ + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ] + ) + ->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords']) + ->getMock(); + + $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(true); + $encryptAll->expects($this->never())->method('createKeyPairs'); + $this->keyManager->expects($this->once())->method('validateMasterKey'); + $encryptAll->expects($this->once())->method('encryptAllUsersFiles')->with(); + $encryptAll->expects($this->never())->method('outputPasswords'); + + $encryptAll->encryptAll($this->inputInterface, $this->outputInterface); + } + + public function testCreateKeyPairs(): void { + /** @var EncryptAll&MockObject $encryptAll */ + $encryptAll = $this->getMockBuilder(EncryptAll::class) + ->setConstructorArgs( + [ + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ] + ) + ->onlyMethods(['setupUserFS', 'generateOneTimePassword']) + ->getMock(); + + + // set protected property $output + $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]); + + $this->keyManager->expects($this->exactly(2))->method('userHasKeys') + ->willReturnCallback( + function ($user) { + if ($user === 'user1') { + return false; + } + return true; + } + ); + + $encryptAll->expects($this->once())->method('setupUserFS')->with('user1'); + $encryptAll->expects($this->once())->method('generateOneTimePassword')->with('user1')->willReturn('password'); + $this->setupUser->expects($this->once())->method('setupUser')->with('user1', 'password'); + + $this->invokePrivate($encryptAll, 'createKeyPairs'); + + $userPasswords = $this->invokePrivate($encryptAll, 'userPasswords'); + + // we only expect the skipped user, because generateOneTimePassword which + // would set the user with the new password was mocked. + // This method will be tested separately + $this->assertSame(1, count($userPasswords)); + $this->assertSame('', $userPasswords['user2']); + } + + public function testEncryptAllUsersFiles(): void { + /** @var EncryptAll&MockObject $encryptAll */ + $encryptAll = $this->getMockBuilder(EncryptAll::class) + ->setConstructorArgs( + [ + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ] + ) + ->onlyMethods(['encryptUsersFiles']) + ->getMock(); + + $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false); + + // set protected property $output + $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]); + $this->invokePrivate($encryptAll, 'userPasswords', [['user1' => 'pwd1', 'user2' => 'pwd2']]); + + $encryptAllCalls = []; + $encryptAll->expects($this->exactly(2)) + ->method('encryptUsersFiles') + ->willReturnCallback(function ($uid) use (&$encryptAllCalls): void { + $encryptAllCalls[] = $uid; + }); + + $this->invokePrivate($encryptAll, 'encryptAllUsersFiles'); + self::assertEquals([ + 'user1', + 'user2', + ], $encryptAllCalls); + } + + public function testEncryptUsersFiles(): void { + /** @var EncryptAll&MockObject $encryptAll */ + $encryptAll = $this->getMockBuilder(EncryptAll::class) + ->setConstructorArgs( + [ + $this->setupUser, + $this->userManager, + $this->view, + $this->keyManager, + $this->util, + $this->config, + $this->mailer, + $this->l, + $this->l10nFactory, + $this->questionHelper, + $this->secureRandom, + $this->logger, + ] + ) + ->onlyMethods(['encryptFile', 'setupUserFS']) + ->getMock(); + + $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false); + + $this->view->expects($this->exactly(2))->method('getDirectoryContent') + ->willReturnMap([ + [ + '/user1/files', + '', + null, + [ + $this->createFileInfoMock(FileInfo::TYPE_FOLDER, 'foo'), + $this->createFileInfoMock(FileInfo::TYPE_FILE, 'bar'), + ], + ], + [ + '/user1/files/foo', + '', + null, + [ + $this->createFileInfoMock(FileInfo::TYPE_FILE, 'subfile'), + ], + ], + ]); + + $encryptAllCalls = []; + $encryptAll->expects($this->exactly(2)) + ->method('encryptFile') + ->willReturnCallback(function (FileInfo $file, string $path) use (&$encryptAllCalls): bool { + $encryptAllCalls[] = $path; + return true; + }); + + $outputFormatter = $this->createMock(OutputFormatterInterface::class); + $outputFormatter->method('isDecorated')->willReturn(false); + $this->outputInterface->expects($this->any()) + ->method('getFormatter') + ->willReturn($outputFormatter); + $progressBar = new ProgressBar($this->outputInterface); + + $this->invokePrivate($encryptAll, 'encryptUsersFiles', ['user1', $progressBar, '']); + self::assertEquals([ + '/user1/files/bar', + '/user1/files/foo/subfile', + ], $encryptAllCalls); + } + + public function testGenerateOneTimePassword(): void { + $password = $this->invokePrivate($this->encryptAll, 'generateOneTimePassword', ['user1']); + $this->assertTrue(is_string($password)); + $this->assertSame(8, strlen($password)); + + $userPasswords = $this->invokePrivate($this->encryptAll, 'userPasswords'); + $this->assertSame(1, count($userPasswords)); + $this->assertSame($password, $userPasswords['user1']); + } + + /** + * @param $isEncrypted + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestEncryptFile')] + public function testEncryptFile($isEncrypted): void { + $fileInfo = $this->createMock(FileInfo::class); + $fileInfo->expects($this->any())->method('isEncrypted') + ->willReturn($isEncrypted); + $this->view->expects($this->never())->method('getFileInfo'); + + + if ($isEncrypted) { + $this->view->expects($this->never())->method('copy'); + $this->view->expects($this->never())->method('rename'); + } else { + $this->view->expects($this->once())->method('copy'); + $this->view->expects($this->once())->method('rename'); + } + + $this->assertTrue( + $this->invokePrivate($this->encryptAll, 'encryptFile', [$fileInfo, 'foo.txt']) + ); + } + + public static function dataTestEncryptFile(): array { + return [ + [true], + [false], + ]; + } +} diff --git a/apps/encryption/tests/lib/crypto/encryptionTest.php b/apps/encryption/tests/Crypto/EncryptionTest.php index 8a228c2c215..37e484550ef 100644 --- a/apps/encryption/tests/lib/crypto/encryptionTest.php +++ b/apps/encryption/tests/Crypto/EncryptionTest.php @@ -1,90 +1,73 @@ <?php + +declare(strict_types=1); + /** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ +namespace OCA\Encryption\Tests\Crypto; -namespace OCA\Encryption\Tests\lib\Crypto; - +use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Files\View; +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\Crypto\DecryptAll; +use OCA\Encryption\Crypto\EncryptAll; +use OCA\Encryption\Crypto\Encryption; use OCA\Encryption\Exceptions\PublicKeyMissingException; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Session; +use OCA\Encryption\Util; +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; -use OCA\Encryption\Crypto\Encryption; class EncryptionTest extends TestCase { - /** @var Encryption */ - private $instance; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $keyManagerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $encryptAllMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $decryptAllMock; + protected Encryption $instance; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $sessionMock; + protected KeyManager&MockObject $keyManagerMock; + protected EncryptAll&MockObject $encryptAllMock; + protected DecryptAll&MockObject $decryptAllMock; + protected Session&MockObject $sessionMock; + protected Crypt&MockObject $cryptMock; + protected Util&MockObject $utilMock; + protected LoggerInterface&MockObject $loggerMock; + protected IL10N&MockObject $l10nMock; + protected IStorage&MockObject $storageMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $cryptMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $utilMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $loggerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $l10nMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $storageMock; - - public function setUp() { + protected function setUp(): void { parent::setUp(); - $this->storageMock = $this->getMockBuilder('OCP\Files\Storage') + $this->storageMock = $this->getMockBuilder(IStorage::class) ->disableOriginalConstructor()->getMock(); - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') + $this->cryptMock = $this->getMockBuilder(Crypt::class) ->disableOriginalConstructor() ->getMock(); - $this->utilMock = $this->getMockBuilder('OCA\Encryption\Util') + $this->utilMock = $this->getMockBuilder(Util::class) ->disableOriginalConstructor() ->getMock(); - $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') + $this->keyManagerMock = $this->getMockBuilder(KeyManager::class) ->disableOriginalConstructor() ->getMock(); - $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); - $this->encryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll') + $this->encryptAllMock = $this->getMockBuilder(EncryptAll::class) ->disableOriginalConstructor() ->getMock(); - $this->decryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\DecryptAll') + $this->decryptAllMock = $this->getMockBuilder(DecryptAll::class) ->disableOriginalConstructor() ->getMock(); - $this->loggerMock = $this->getMockBuilder('OCP\ILogger') + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->l10nMock = $this->getMockBuilder('OCP\IL10N') + $this->l10nMock = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor() ->getMock(); $this->l10nMock->expects($this->any()) @@ -102,24 +85,32 @@ class EncryptionTest extends TestCase { $this->loggerMock, $this->l10nMock ); - } /** * test if public key from one of the recipients is missing */ - public function testEndUser1() { - $this->instance->begin('/foo/bar', 'user1', 'r', array(), array('users' => array('user1', 'user2', 'user3'))); + public function testEndUser1(): void { + $this->sessionMock->expects($this->once()) + ->method('decryptAllModeActivated') + ->willReturn(false); + + $this->instance->begin('/foo/bar', 'user1', 'r', [], ['users' => ['user1', 'user2', 'user3']]); $this->endTest(); } /** * test if public key from owner is missing * - * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException */ - public function testEndUser2() { - $this->instance->begin('/foo/bar', 'user2', 'r', array(), array('users' => array('user1', 'user2', 'user3'))); + public function testEndUser2(): void { + $this->sessionMock->expects($this->once()) + ->method('decryptAllModeActivated') + ->willReturn(false); + + $this->expectException(PublicKeyMissingException::class); + + $this->instance->begin('/foo/bar', 'user2', 'r', [], ['users' => ['user1', 'user2', 'user3']]); $this->endTest(); } @@ -135,16 +126,13 @@ class EncryptionTest extends TestCase { $this->keyManagerMock->expects($this->any()) ->method('getPublicKey') - ->will($this->returnCallback([$this, 'getPublicKeyCallback'])); + ->willReturnCallback([$this, 'getPublicKeyCallback']); $this->keyManagerMock->expects($this->any()) ->method('addSystemKeys') - ->will($this->returnCallback([$this, 'addSystemKeysCallback'])); + ->willReturnCallback([$this, 'addSystemKeysCallback']); $this->cryptMock->expects($this->any()) ->method('multiKeyEncrypt') - ->willReturn(true); - $this->cryptMock->expects($this->any()) - ->method('setAllFileKeys') - ->willReturn(true); + ->willReturn([]); $this->instance->end('/foo/bar'); } @@ -164,29 +152,24 @@ class EncryptionTest extends TestCase { return $publicKeys; } - /** - * @dataProvider dataProviderForTestGetPathToRealFile - */ - public function testGetPathToRealFile($path, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderForTestGetPathToRealFile')] + public function testGetPathToRealFile($path, $expected): void { $this->assertSame($expected, - self::invokePrivate($this->instance, 'getPathToRealFile', array($path)) + self::invokePrivate($this->instance, 'getPathToRealFile', [$path]) ); } - public function dataProviderForTestGetPathToRealFile() { - return array( - array('/user/files/foo/bar.txt', '/user/files/foo/bar.txt'), - array('/user/files/foo.txt', '/user/files/foo.txt'), - array('/user/files_versions/foo.txt.v543534', '/user/files/foo.txt'), - array('/user/files_versions/foo/bar.txt.v5454', '/user/files/foo/bar.txt'), - ); + public static function dataProviderForTestGetPathToRealFile(): array { + return [ + ['/user/files/foo/bar.txt', '/user/files/foo/bar.txt'], + ['/user/files/foo.txt', '/user/files/foo.txt'], + ['/user/files_versions/foo.txt.v543534', '/user/files/foo.txt'], + ['/user/files_versions/foo/bar.txt.v5454', '/user/files/foo/bar.txt'], + ]; } - /** - * @dataProvider dataTestBegin - */ - public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected) { - + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestBegin')] + public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected): void { $this->sessionMock->expects($this->once()) ->method('decryptAllModeActivated') ->willReturn(false); @@ -227,51 +210,31 @@ class EncryptionTest extends TestCase { } } - public function dataTestBegin() { - return array( - array('w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'defaultCipher'), - array('r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'), - array('w', [], 'legacyCipher', 'defaultCipher', '', 'defaultCipher'), - array('r', [], 'legacyCipher', 'defaultCipher', 'file_key', 'legacyCipher'), - ); + public static function dataTestBegin(): array { + return [ + ['w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'defaultCipher'], + ['r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'], + ['w', [], 'legacyCipher', 'defaultCipher', '', 'defaultCipher'], + ['r', [], 'legacyCipher', 'defaultCipher', 'file_key', 'legacyCipher'], + ]; } /** * test begin() if decryptAll mode was activated */ - public function testBeginDecryptAll() { - + public function testBeginDecryptAll(): void { $path = '/user/files/foo.txt'; - $recoveryKeyId = 'recoveryKeyId'; - $recoveryShareKey = 'recoveryShareKey'; - $decryptAllKey = 'decryptAllKey'; $fileKey = 'fileKey'; $this->sessionMock->expects($this->once()) ->method('decryptAllModeActivated') ->willReturn(true); - $this->sessionMock->expects($this->once()) - ->method('getDecryptAllUid') - ->willReturn($recoveryKeyId); - $this->sessionMock->expects($this->once()) - ->method('getDecryptAllKey') - ->willReturn($decryptAllKey); - - $this->keyManagerMock->expects($this->once()) - ->method('getEncryptedFileKey') - ->willReturn('encryptedFileKey'); $this->keyManagerMock->expects($this->once()) - ->method('getShareKey') - ->with($path, $recoveryKeyId) - ->willReturn($recoveryShareKey); - $this->cryptMock->expects($this->once()) - ->method('multiKeyDecrypt') - ->with('encryptedFileKey', $recoveryShareKey, $decryptAllKey) + ->method('getFileKey') + ->with($path, 'user', null, true) ->willReturn($fileKey); - $this->keyManagerMock->expects($this->never())->method('getFileKey'); - $this->instance->begin($path, 'user', 'r', [], []); $this->assertSame($fileKey, @@ -280,12 +243,30 @@ class EncryptionTest extends TestCase { } /** - * @dataProvider dataTestUpdate + * test begin() if encryption is not initialized but the master key is enabled + * in this case we can initialize the encryption without a username/password + * and continue + */ + public function testBeginInitMasterKey(): void { + $this->sessionMock->expects($this->once()) + ->method('decryptAllModeActivated') + ->willReturn(false); + + $this->sessionMock->expects($this->once())->method('isReady')->willReturn(false); + $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + $this->keyManagerMock->expects($this->once())->method('init')->with('', ''); + + $this->instance->begin('/user/files/welcome.txt', 'user', 'r', [], []); + } + + /** * * @param string $fileKey * @param boolean $expected */ - public function testUpdate($fileKey, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUpdate')] + public function testUpdate($fileKey, $expected): void { $this->keyManagerMock->expects($this->once()) ->method('getFileKey')->willReturn($fileKey); @@ -294,7 +275,7 @@ class EncryptionTest extends TestCase { $this->keyManagerMock->expects($this->any()) ->method('addSystemKeys') - ->willReturnCallback(function($accessList, $publicKeys) { + ->willReturnCallback(function ($accessList, $publicKeys) { return $publicKeys; }); @@ -306,55 +287,55 @@ class EncryptionTest extends TestCase { ); } - public function dataTestUpdate() { - return array( - array('', false), - array('fileKey', true) - ); + public static function dataTestUpdate(): array { + return [ + ['', false], + ['fileKey', true] + ]; } - public function testUpdateNoUsers() { - + public function testUpdateNoUsers(): void { $this->invokePrivate($this->instance, 'rememberVersion', [['path' => 2]]); $this->keyManagerMock->expects($this->never())->method('getFileKey'); $this->keyManagerMock->expects($this->never())->method('getPublicKey'); $this->keyManagerMock->expects($this->never())->method('addSystemKeys'); $this->keyManagerMock->expects($this->once())->method('setVersion') - ->willReturnCallback(function($path, $version, $view) { + ->willReturnCallback(function ($path, $version, $view): void { $this->assertSame('path', $path); $this->assertSame(2, $version); - $this->assertTrue($view instanceof \OC\Files\View); + $this->assertTrue($view instanceof View); }); $this->instance->update('path', 'user1', []); } /** - * Test case if the public key is missing. ownCloud should still encrypt + * Test case if the public key is missing. Nextcloud should still encrypt * the file for the remaining users */ - public function testUpdateMissingPublicKey() { + public function testUpdateMissingPublicKey(): void { $this->keyManagerMock->expects($this->once()) ->method('getFileKey')->willReturn('fileKey'); $this->keyManagerMock->expects($this->any()) ->method('getPublicKey')->willReturnCallback( - function($user) { + function ($user): void { throw new PublicKeyMissingException($user); } ); $this->keyManagerMock->expects($this->any()) ->method('addSystemKeys') - ->willReturnCallback(function($accessList, $publicKeys) { + ->willReturnCallback(function ($accessList, $publicKeys) { return $publicKeys; }); $this->cryptMock->expects($this->once())->method('multiKeyEncrypt') ->willReturnCallback( - function($fileKey, $publicKeys) { + function ($fileKey, $publicKeys) { $this->assertEmpty($publicKeys); $this->assertSame('fileKey', $fileKey); + return []; } ); @@ -369,10 +350,9 @@ class EncryptionTest extends TestCase { /** * by default the encryption module should encrypt regular files, files in * files_versions and files in files_trashbin - * - * @dataProvider dataTestShouldEncrypt */ - public function testShouldEncrypt($path, $shouldEncryptHomeStorage, $isHomeStorage, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')] + public function testShouldEncrypt($path, $shouldEncryptHomeStorage, $isHomeStorage, $expected): void { $this->utilMock->expects($this->once())->method('shouldEncryptHomeStorage') ->willReturn($shouldEncryptHomeStorage); @@ -388,38 +368,39 @@ class EncryptionTest extends TestCase { ); } - public function dataTestShouldEncrypt() { - return array( - array('/user1/files/foo.txt', true, true, true), - array('/user1/files_versions/foo.txt', true, true, true), - array('/user1/files_trashbin/foo.txt', true, true, true), - array('/user1/some_folder/foo.txt', true, true, false), - array('/user1/foo.txt', true, true, false), - array('/user1/files', true, true, false), - array('/user1/files_trashbin', true, true, false), - array('/user1/files_versions', true, true, false), + public static function dataTestShouldEncrypt(): array { + return [ + ['/user1/files/foo.txt', true, true, true], + ['/user1/files_versions/foo.txt', true, true, true], + ['/user1/files_trashbin/foo.txt', true, true, true], + ['/user1/some_folder/foo.txt', true, true, false], + ['/user1/foo.txt', true, true, false], + ['/user1/files', true, true, false], + ['/user1/files_trashbin', true, true, false], + ['/user1/files_versions', true, true, false], // test if shouldEncryptHomeStorage is set to false - array('/user1/files/foo.txt', false, true, false), - array('/user1/files_versions/foo.txt', false, false, true), - ); + ['/user1/files/foo.txt', false, true, false], + ['/user1/files_versions/foo.txt', false, false, true], + ]; } - /** - * @expectedException \OC\Encryption\Exceptions\DecryptionFailedException - * @expectedExceptionMessage Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you. - */ - public function testDecrypt() { + + public function testDecrypt(): void { + $this->expectException(DecryptionFailedException::class); + $this->expectExceptionMessage('Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.'); + $this->instance->decrypt('abc'); } - public function testPrepareDecryptAll() { - $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); - $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + public function testPrepareDecryptAll(): void { + /** @var \Symfony\Component\Console\Input\InputInterface $input */ + $input = $this->createMock(InputInterface::class); + /** @var \Symfony\Component\Console\Output\OutputInterface $output */ + $output = $this->createMock(OutputInterface::class); $this->decryptAllMock->expects($this->once())->method('prepare') ->with($input, $output, 'user'); $this->instance->prepareDecryptAll($input, $output, 'user'); } - } diff --git a/apps/encryption/tests/EncryptedStorageTest.php b/apps/encryption/tests/EncryptedStorageTest.php new file mode 100644 index 00000000000..59f419a7f7a --- /dev/null +++ b/apps/encryption/tests/EncryptedStorageTest.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\encryption\tests; + +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\Encryption; +use OC\Files\View; +use OCP\Files\Mount\IMountManager; +use OCP\Files\Storage\IDisableEncryptionStorage; +use OCP\Server; +use Test\TestCase; +use Test\Traits\EncryptionTrait; +use Test\Traits\MountProviderTrait; +use Test\Traits\UserTrait; + +class TemporaryNoEncrypted extends Temporary implements IDisableEncryptionStorage { + +} + +/** + * @group DB + */ +class EncryptedStorageTest extends TestCase { + use MountProviderTrait; + use EncryptionTrait; + use UserTrait; + + public function testMoveFromEncrypted(): void { + $this->createUser('test1', 'test2'); + $this->setupForUser('test1', 'test2'); + + $unwrapped = new Temporary(); + + $this->registerMount('test1', new TemporaryNoEncrypted(), '/test1/files/unenc'); + $this->registerMount('test1', $unwrapped, '/test1/files/enc'); + + $this->loginWithEncryption('test1'); + + $view = new View('/test1/files'); + + /** @var IMountManager $mountManager */ + $mountManager = Server::get(IMountManager::class); + + $encryptedMount = $mountManager->find('/test1/files/enc'); + $unencryptedMount = $mountManager->find('/test1/files/unenc'); + $encryptedStorage = $encryptedMount->getStorage(); + $unencryptedStorage = $unencryptedMount->getStorage(); + $encryptedCache = $encryptedStorage->getCache(); + $unencryptedCache = $unencryptedStorage->getCache(); + + $this->assertTrue($encryptedStorage->instanceOfStorage(Encryption::class)); + $this->assertFalse($unencryptedStorage->instanceOfStorage(Encryption::class)); + + $encryptedStorage->file_put_contents('foo.txt', 'bar'); + $this->assertEquals('bar', $encryptedStorage->file_get_contents('foo.txt')); + $this->assertStringStartsWith('HBEGIN:oc_encryption_module:', $unwrapped->file_get_contents('foo.txt')); + + $this->assertTrue($encryptedCache->get('foo.txt')->isEncrypted()); + + $view->rename('enc/foo.txt', 'unenc/foo.txt'); + + $this->assertEquals('bar', $unencryptedStorage->file_get_contents('foo.txt')); + $this->assertFalse($unencryptedCache->get('foo.txt')->isEncrypted()); + } +} diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/KeyManagerTest.php index 7ede6177deb..3fe76fc4f59 100644 --- a/apps/encryption/tests/lib/KeyManagerTest.php +++ b/apps/encryption/tests/KeyManagerTest.php @@ -1,89 +1,70 @@ <?php + +declare(strict_types=1); + /** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Lukas Reschke <lukas@owncloud.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Encryption\Tests; - +use OC\Files\FileInfo; +use OC\Files\View; +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\Crypto\Encryption; +use OCA\Encryption\Exceptions\PrivateKeyMissingException; +use OCA\Encryption\Exceptions\PublicKeyMissingException; use OCA\Encryption\KeyManager; use OCA\Encryption\Session; +use OCA\Encryption\Util; +use OCP\Encryption\Keys\IStorage; +use OCP\Files\Cache\ICache; +use OCP\Files\Storage\IStorage as FilesIStorage; +use OCP\IConfig; +use OCP\IUserSession; +use OCP\Lock\ILockingProvider; +use OCP\Lock\LockedException; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Test\TestCase; class KeyManagerTest extends TestCase { - /** - * @var KeyManager - */ - private $instance; - /** - * @var string - */ - private $userId; - - /** @var string */ - private $systemKeyId; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $keyStorageMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $cryptMock; + protected KeyManager $instance; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $userMock; + protected string $userId; + protected string $systemKeyId; + protected IStorage&MockObject $keyStorageMock; + protected Crypt&MockObject $cryptMock; + protected IUserSession&MockObject $userMock; + protected Session&MockObject $sessionMock; + protected LoggerInterface&MockObject $logMock; + protected Util&MockObject $utilMock; + protected IConfig&MockObject $configMock; + protected ILockingProvider&MockObject $lockingProviderMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $sessionMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $logMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $utilMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $configMock; - - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->userId = 'user1'; $this->systemKeyId = 'systemKeyId'; - $this->keyStorageMock = $this->getMock('OCP\Encryption\Keys\IStorage'); - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') + $this->keyStorageMock = $this->createMock(IStorage::class); + $this->cryptMock = $this->getMockBuilder(Crypt::class) ->disableOriginalConstructor() ->getMock(); - $this->configMock = $this->getMock('OCP\IConfig'); + $this->configMock = $this->createMock(IConfig::class); $this->configMock->expects($this->any()) ->method('getAppValue') ->willReturn($this->systemKeyId); - $this->userMock = $this->getMock('OCP\IUserSession'); - $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') + $this->userMock = $this->createMock(IUserSession::class); + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); - $this->logMock = $this->getMock('OCP\ILogger'); - $this->utilMock = $this->getMockBuilder('OCA\Encryption\Util') + $this->logMock = $this->createMock(LoggerInterface::class); + $this->utilMock = $this->getMockBuilder(Util::class) ->disableOriginalConstructor() ->getMock(); + $this->lockingProviderMock = $this->createMock(ILockingProvider::class); $this->instance = new KeyManager( $this->keyStorageMock, @@ -92,10 +73,12 @@ class KeyManagerTest extends TestCase { $this->userMock, $this->sessionMock, $this->logMock, - $this->utilMock); + $this->utilMock, + $this->lockingProviderMock + ); } - public function testDeleteShareKey() { + public function testDeleteShareKey(): void { $this->keyStorageMock->expects($this->any()) ->method('deleteFileKey') ->with($this->equalTo('/path'), $this->equalTo('keyId.shareKey')) @@ -106,7 +89,7 @@ class KeyManagerTest extends TestCase { ); } - public function testGetPrivateKey() { + public function testGetPrivateKey(): void { $this->keyStorageMock->expects($this->any()) ->method('getUserKey') ->with($this->equalTo($this->userId), $this->equalTo('privateKey')) @@ -118,7 +101,7 @@ class KeyManagerTest extends TestCase { ); } - public function testGetPublicKey() { + public function testGetPublicKey(): void { $this->keyStorageMock->expects($this->any()) ->method('getUserKey') ->with($this->equalTo($this->userId), $this->equalTo('publicKey')) @@ -130,7 +113,7 @@ class KeyManagerTest extends TestCase { ); } - public function testRecoveryKeyExists() { + public function testRecoveryKeyExists(): void { $this->keyStorageMock->expects($this->any()) ->method('getSystemUserKey') ->with($this->equalTo($this->systemKeyId . '.publicKey')) @@ -140,7 +123,7 @@ class KeyManagerTest extends TestCase { $this->assertTrue($this->instance->recoveryKeyExists()); } - public function testCheckRecoveryKeyPassword() { + public function testCheckRecoveryKeyPassword(): void { $this->keyStorageMock->expects($this->any()) ->method('getSystemUserKey') ->with($this->equalTo($this->systemKeyId . '.privateKey')) @@ -153,7 +136,7 @@ class KeyManagerTest extends TestCase { $this->assertTrue($this->instance->checkRecoveryPassword('pass')); } - public function testSetPublicKey() { + public function testSetPublicKey(): void { $this->keyStorageMock->expects($this->any()) ->method('setUserKey') ->with( @@ -168,7 +151,7 @@ class KeyManagerTest extends TestCase { ); } - public function testSetPrivateKey() { + public function testSetPrivateKey(): void { $this->keyStorageMock->expects($this->any()) ->method('setUserKey') ->with( @@ -183,10 +166,8 @@ class KeyManagerTest extends TestCase { ); } - /** - * @dataProvider dataTestUserHasKeys - */ - public function testUserHasKeys($key, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUserHasKeys')] + public function testUserHasKeys($key, $expected): void { $this->keyStorageMock->expects($this->exactly(2)) ->method('getUserKey') ->with($this->equalTo($this->userId), $this->anything()) @@ -198,21 +179,21 @@ class KeyManagerTest extends TestCase { ); } - public function dataTestUserHasKeys() { + public static function dataTestUserHasKeys(): array { return [ ['key', true], ['', false] ]; } - /** - * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException - */ - public function testUserHasKeysMissingPrivateKey() { + + public function testUserHasKeysMissingPrivateKey(): void { + $this->expectException(PrivateKeyMissingException::class); + $this->keyStorageMock->expects($this->exactly(2)) ->method('getUserKey') ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) { - if ($keyID=== 'privateKey') { + if ($keyID === 'privateKey') { return ''; } return 'key'; @@ -221,13 +202,13 @@ class KeyManagerTest extends TestCase { $this->instance->userHasKeys($this->userId); } - /** - * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException - */ - public function testUserHasKeysMissingPublicKey() { + + public function testUserHasKeysMissingPublicKey(): void { + $this->expectException(PublicKeyMissingException::class); + $this->keyStorageMock->expects($this->exactly(2)) ->method('getUserKey') - ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId){ + ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) { if ($keyID === 'publicKey') { return ''; } @@ -235,17 +216,15 @@ class KeyManagerTest extends TestCase { }); $this->instance->userHasKeys($this->userId); - } /** - * @dataProvider dataTestInit - * * @param bool $useMasterKey */ - public function testInit($useMasterKey) { - - $instance = $this->getMockBuilder('OCA\Encryption\KeyManager') + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestInit')] + public function testInit($useMasterKey): void { + /** @var KeyManager&MockObject $instance */ + $instance = $this->getMockBuilder(KeyManager::class) ->setConstructorArgs( [ $this->keyStorageMock, @@ -254,23 +233,28 @@ class KeyManagerTest extends TestCase { $this->userMock, $this->sessionMock, $this->logMock, - $this->utilMock + $this->utilMock, + $this->lockingProviderMock ] - )->setMethods(['getMasterKeyId', 'getMasterKeyPassword', 'getSystemPrivateKey', 'getPrivateKey']) + )->onlyMethods(['getMasterKeyId', 'getMasterKeyPassword', 'getSystemPrivateKey', 'getPrivateKey']) ->getMock(); $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') ->willReturn($useMasterKey); - $this->sessionMock->expects($this->at(0))->method('setStatus') - ->with(Session::INIT_EXECUTED); + $sessionSetStatusCalls = []; + $this->sessionMock->expects($this->exactly(2)) + ->method('setStatus') + ->willReturnCallback(function (string $status) use (&$sessionSetStatusCalls): void { + $sessionSetStatusCalls[] = $status; + }); $instance->expects($this->any())->method('getMasterKeyId')->willReturn('masterKeyId'); $instance->expects($this->any())->method('getMasterKeyPassword')->willReturn('masterKeyPassword'); $instance->expects($this->any())->method('getSystemPrivateKey')->with('masterKeyId')->willReturn('privateMasterKey'); $instance->expects($this->any())->method('getPrivateKey')->with($this->userId)->willReturn('privateUserKey'); - if($useMasterKey) { + if ($useMasterKey) { $this->cryptMock->expects($this->once())->method('decryptPrivateKey') ->with('privateMasterKey', 'masterKeyPassword', 'masterKeyId') ->willReturn('key'); @@ -284,9 +268,13 @@ class KeyManagerTest extends TestCase { ->with('key'); $this->assertTrue($instance->init($this->userId, 'pass')); + self::assertEquals([ + Session::INIT_EXECUTED, + Session::INIT_SUCCESSFUL, + ], $sessionSetStatusCalls); } - public function dataTestInit() { + public static function dataTestInit(): array { return [ [true], [false] @@ -294,7 +282,7 @@ class KeyManagerTest extends TestCase { } - public function testSetRecoveryKey() { + public function testSetRecoveryKey(): void { $this->keyStorageMock->expects($this->exactly(2)) ->method('setSystemUserKey') ->willReturn(true); @@ -306,11 +294,11 @@ class KeyManagerTest extends TestCase { $this->assertTrue( $this->instance->setRecoveryKey('pass', - array('publicKey' => 'publicKey', 'privateKey' => 'privateKey')) + ['publicKey' => 'publicKey', 'privateKey' => 'privateKey']) ); } - public function testSetSystemPrivateKey() { + public function testSetSystemPrivateKey(): void { $this->keyStorageMock->expects($this->exactly(1)) ->method('setSystemUserKey') ->with($this->equalTo('keyId.privateKey'), $this->equalTo('key')) @@ -322,7 +310,7 @@ class KeyManagerTest extends TestCase { ); } - public function testGetSystemPrivateKey() { + public function testGetSystemPrivateKey(): void { $this->keyStorageMock->expects($this->exactly(1)) ->method('getSystemUserKey') ->with($this->equalTo('keyId.privateKey')) @@ -334,7 +322,7 @@ class KeyManagerTest extends TestCase { ); } - public function testGetEncryptedFileKey() { + public function testGetEncryptedFileKey(): void { $this->keyStorageMock->expects($this->once()) ->method('getFileKey') ->with('/', 'fileKey') @@ -343,35 +331,59 @@ class KeyManagerTest extends TestCase { $this->assertTrue($this->instance->getEncryptedFileKey('/')); } + public static function dataTestGetFileKey(): array { + return [ + ['user1', false, 'privateKey', 'legacyKey', 'multiKeyDecryptResult'], + ['user1', false, 'privateKey', '', 'multiKeyDecryptResult'], + ['user1', false, false, 'legacyKey', ''], + ['user1', false, false, '', ''], + ['user1', true, 'privateKey', 'legacyKey', 'multiKeyDecryptResult'], + ['user1', true, 'privateKey', '', 'multiKeyDecryptResult'], + ['user1', true, false, 'legacyKey', ''], + ['user1', true, false, '', ''], + [null, false, 'privateKey', 'legacyKey', 'multiKeyDecryptResult'], + [null, false, 'privateKey', '', 'multiKeyDecryptResult'], + [null, false, false, 'legacyKey', ''], + [null, false, false, '', ''], + [null, true, 'privateKey', 'legacyKey', 'multiKeyDecryptResult'], + [null, true, 'privateKey', '', 'multiKeyDecryptResult'], + [null, true, false, 'legacyKey', ''], + [null, true, false, '', ''], + ]; + } + /** - * @dataProvider dataTestGetFileKey * * @param $uid * @param $isMasterKeyEnabled * @param $privateKey * @param $expected */ - public function testGetFileKey($uid, $isMasterKeyEnabled, $privateKey, $expected) { - + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetFileKey')] + public function testGetFileKey($uid, $isMasterKeyEnabled, $privateKey, $encryptedFileKey, $expected): void { $path = '/foo.txt'; if ($isMasterKeyEnabled) { $expectedUid = 'masterKeyId'; + $this->configMock->expects($this->any())->method('getSystemValue')->with('secret') + ->willReturn('password'); + } elseif (!$uid) { + $expectedUid = 'systemKeyId'; } else { $expectedUid = $uid; } $this->invokePrivate($this->instance, 'masterKeyId', ['masterKeyId']); - $this->keyStorageMock->expects($this->at(0)) + $this->keyStorageMock->expects($this->exactly(2)) ->method('getFileKey') - ->with($path, 'fileKey', 'OC_DEFAULT_MODULE') - ->willReturn(true); + ->willReturnMap([ + [$path, 'fileKey', 'OC_DEFAULT_MODULE', $encryptedFileKey], + [$path, $expectedUid . '.shareKey', 'OC_DEFAULT_MODULE', 'fileKey'], + ]); - $this->keyStorageMock->expects($this->at(1)) - ->method('getFileKey') - ->with($path, $expectedUid . '.shareKey', 'OC_DEFAULT_MODULE') - ->willReturn(true); + $this->utilMock->expects($this->any())->method('isMasterKeyEnabled') + ->willReturn($isMasterKeyEnabled); if (is_null($uid)) { $this->keyStorageMock->expects($this->once()) @@ -383,40 +395,39 @@ class KeyManagerTest extends TestCase { } else { $this->keyStorageMock->expects($this->never()) ->method('getSystemUserKey'); - $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') - ->willReturn($isMasterKeyEnabled); $this->sessionMock->expects($this->once())->method('getPrivateKey')->willReturn($privateKey); } - if($privateKey) { - $this->cryptMock->expects($this->once()) - ->method('multiKeyDecrypt') - ->willReturn(true); - } else { + if (!empty($encryptedFileKey)) { $this->cryptMock->expects($this->never()) ->method('multiKeyDecrypt'); + if ($privateKey) { + $this->cryptMock->expects($this->once()) + ->method('multiKeyDecryptLegacy') + ->willReturn('multiKeyDecryptResult'); + } else { + $this->cryptMock->expects($this->never()) + ->method('multiKeyDecryptLegacy'); + } + } else { + $this->cryptMock->expects($this->never()) + ->method('multiKeyDecryptLegacy'); + if ($privateKey) { + $this->cryptMock->expects($this->once()) + ->method('multiKeyDecrypt') + ->willReturn('multiKeyDecryptResult'); + } else { + $this->cryptMock->expects($this->never()) + ->method('multiKeyDecrypt'); + } } $this->assertSame($expected, - $this->instance->getFileKey($path, $uid) + $this->instance->getFileKey($path, $uid, null) ); - } - public function dataTestGetFileKey() { - return [ - ['user1', false, 'privateKey', true], - ['user1', false, false, ''], - ['user1', true, 'privateKey', true], - ['user1', true, false, ''], - ['', false, 'privateKey', true], - ['', false, false, ''], - ['', true, 'privateKey', true], - ['', true, false, ''] - ]; - } - - public function testDeletePrivateKey() { + public function testDeletePrivateKey(): void { $this->keyStorageMock->expects($this->once()) ->method('deleteUserKey') ->with('user1', 'privateKey') @@ -427,7 +438,7 @@ class KeyManagerTest extends TestCase { [$this->userId])); } - public function testDeleteAllFileKeys() { + public function testDeleteAllFileKeys(): void { $this->keyStorageMock->expects($this->once()) ->method('deleteAllFileKeys') ->willReturn(true); @@ -438,27 +449,26 @@ class KeyManagerTest extends TestCase { /** * test add public share key and or recovery key to the list of public keys * - * @dataProvider dataTestAddSystemKeys * * @param array $accessList * @param array $publicKeys * @param string $uid * @param array $expectedKeys */ - public function testAddSystemKeys($accessList, $publicKeys, $uid, $expectedKeys) { - + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestAddSystemKeys')] + public function testAddSystemKeys($accessList, $publicKeys, $uid, $expectedKeys): void { $publicShareKeyId = 'publicShareKey'; $recoveryKeyId = 'recoveryKey'; $this->keyStorageMock->expects($this->any()) ->method('getSystemUserKey') - ->willReturnCallback(function($keyId, $encryptionModuleId) { + ->willReturnCallback(function ($keyId, $encryptionModuleId) { return $keyId; }); $this->utilMock->expects($this->any()) ->method('isRecoveryEnabledForUser') - ->willReturnCallback(function($uid) { + ->willReturnCallback(function ($uid) { if ($uid === 'user1') { return true; } @@ -483,22 +493,22 @@ class KeyManagerTest extends TestCase { * * @return array */ - public function dataTestAddSystemKeys() { - return array( - array(['public' => true],[], 'user1', ['publicShareKey', 'recoveryKey']), - array(['public' => false], [], 'user1', ['recoveryKey']), - array(['public' => true],[], 'user2', ['publicShareKey']), - array(['public' => false], [], 'user2', []), - ); + public static function dataTestAddSystemKeys(): array { + return [ + [['public' => true],[], 'user1', ['publicShareKey', 'recoveryKey']], + [['public' => false], [], 'user1', ['recoveryKey']], + [['public' => true],[], 'user2', ['publicShareKey']], + [['public' => false], [], 'user2', []], + ]; } - public function testGetMasterKeyId() { + public function testGetMasterKeyId(): void { $this->assertSame('systemKeyId', $this->instance->getMasterKeyId()); } - public function testGetPublicMasterKey() { + public function testGetPublicMasterKey(): void { $this->keyStorageMock->expects($this->once())->method('getSystemUserKey') - ->with('systemKeyId.publicKey', \OCA\Encryption\Crypto\Encryption::ID) + ->with('systemKeyId.publicKey', Encryption::ID) ->willReturn(true); $this->assertTrue( @@ -506,7 +516,7 @@ class KeyManagerTest extends TestCase { ); } - public function testGetMasterKeyPassword() { + public function testGetMasterKeyPassword(): void { $this->configMock->expects($this->once())->method('getSystemValue')->with('secret') ->willReturn('password'); @@ -515,10 +525,10 @@ class KeyManagerTest extends TestCase { ); } - /** - * @expectedException \Exception - */ - public function testGetMasterKeyPasswordException() { + + public function testGetMasterKeyPasswordException(): void { + $this->expectException(\Exception::class); + $this->configMock->expects($this->once())->method('getSystemValue')->with('secret') ->willReturn(''); @@ -526,14 +536,12 @@ class KeyManagerTest extends TestCase { } /** - * @dataProvider dataTestValidateMasterKey - * * @param $masterKey */ - public function testValidateMasterKey($masterKey) { - - /** @var \OCA\Encryption\KeyManager | \PHPUnit_Framework_MockObject_MockObject $instance */ - $instance = $this->getMockBuilder('OCA\Encryption\KeyManager') + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestValidateMasterKey')] + public function testValidateMasterKey($masterKey): void { + /** @var KeyManager&MockObject $instance */ + $instance = $this->getMockBuilder(KeyManager::class) ->setConstructorArgs( [ $this->keyStorageMock, @@ -542,25 +550,31 @@ class KeyManagerTest extends TestCase { $this->userMock, $this->sessionMock, $this->logMock, - $this->utilMock + $this->utilMock, + $this->lockingProviderMock ] - )->setMethods(['getPublicMasterKey', 'setSystemPrivateKey', 'getMasterKeyPassword']) + )->onlyMethods(['getPublicMasterKey', 'setSystemPrivateKey', 'getMasterKeyPassword']) ->getMock(); + $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + $instance->expects($this->once())->method('getPublicMasterKey') ->willReturn($masterKey); $instance->expects($this->any())->method('getMasterKeyPassword')->willReturn('masterKeyPassword'); $this->cryptMock->expects($this->any())->method('generateHeader')->willReturn('header'); - if(empty($masterKey)) { + if (empty($masterKey)) { $this->cryptMock->expects($this->once())->method('createKeyPair') ->willReturn(['publicKey' => 'public', 'privateKey' => 'private']); $this->keyStorageMock->expects($this->once())->method('setSystemUserKey') - ->with('systemKeyId.publicKey', 'public', \OCA\Encryption\Crypto\Encryption::ID); + ->with('systemKeyId.publicKey', 'public', Encryption::ID); $this->cryptMock->expects($this->once())->method('encryptPrivateKey') ->with('private', 'masterKeyPassword', 'systemKeyId') ->willReturn('EncryptedKey'); + $this->lockingProviderMock->expects($this->once()) + ->method('acquireLock'); $instance->expects($this->once())->method('setSystemPrivateKey') ->with('systemKeyId', 'headerEncryptedKey'); } else { @@ -573,28 +587,64 @@ class KeyManagerTest extends TestCase { $instance->validateMasterKey(); } - public function dataTestValidateMasterKey() { + public function testValidateMasterKeyLocked(): void { + /** @var KeyManager&MockObject $instance */ + $instance = $this->getMockBuilder(KeyManager::class) + ->setConstructorArgs([ + $this->keyStorageMock, + $this->cryptMock, + $this->configMock, + $this->userMock, + $this->sessionMock, + $this->logMock, + $this->utilMock, + $this->lockingProviderMock + ]) + ->onlyMethods(['getPublicMasterKey', 'getPrivateMasterKey', 'setSystemPrivateKey', 'getMasterKeyPassword']) + ->getMock(); + + $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $instance->expects($this->once())->method('getPublicMasterKey') + ->willReturn(''); + $instance->expects($this->once())->method('getPrivateMasterKey') + ->willReturn(''); + + $instance->expects($this->any())->method('getMasterKeyPassword')->willReturn('masterKeyPassword'); + $this->cryptMock->expects($this->any())->method('generateHeader')->willReturn('header'); + + $this->lockingProviderMock->expects($this->once()) + ->method('acquireLock') + ->willThrowException(new LockedException('encryption-generateMasterKey')); + + $this->expectException(LockedException::class); + $instance->validateMasterKey(); + } + + public static function dataTestValidateMasterKey(): array { return [ ['masterKey'], [''] ]; } - public function testGetVersionWithoutFileInfo() { - $view = $this->getMockBuilder('\\OC\\Files\\View') + public function testGetVersionWithoutFileInfo(): void { + $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor()->getMock(); $view->expects($this->once()) ->method('getFileInfo') ->with('/admin/files/myfile.txt') ->willReturn(false); + /** @var View $view */ $this->assertSame(0, $this->instance->getVersion('/admin/files/myfile.txt', $view)); } - public function testGetVersionWithFileInfo() { - $view = $this->getMockBuilder('\\OC\\Files\\View') + public function testGetVersionWithFileInfo(): void { + $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor()->getMock(); - $fileInfo = $this->getMockBuilder('\\OC\\Files\\FileInfo') + $fileInfo = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor()->getMock(); $fileInfo->expects($this->once()) ->method('getEncryptedVersion') @@ -604,23 +654,24 @@ class KeyManagerTest extends TestCase { ->with('/admin/files/myfile.txt') ->willReturn($fileInfo); + /** @var View $view */ $this->assertSame(1337, $this->instance->getVersion('/admin/files/myfile.txt', $view)); } - public function testSetVersionWithFileInfo() { - $view = $this->getMockBuilder('\\OC\\Files\\View') + public function testSetVersionWithFileInfo(): void { + $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor()->getMock(); - $cache = $this->getMockBuilder('\\OCP\\Files\\Cache\\ICache') + $cache = $this->getMockBuilder(ICache::class) ->disableOriginalConstructor()->getMock(); $cache->expects($this->once()) ->method('update') ->with(123, ['encrypted' => 5, 'encryptedVersion' => 5]); - $storage = $this->getMockBuilder('\\OCP\\Files\\Storage') + $storage = $this->getMockBuilder(FilesIStorage::class) ->disableOriginalConstructor()->getMock(); $storage->expects($this->once()) ->method('getCache') ->willReturn($cache); - $fileInfo = $this->getMockBuilder('\\OC\\Files\\FileInfo') + $fileInfo = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor()->getMock(); $fileInfo->expects($this->once()) ->method('getStorage') @@ -633,18 +684,25 @@ class KeyManagerTest extends TestCase { ->with('/admin/files/myfile.txt') ->willReturn($fileInfo); + /** @var View $view */ $this->instance->setVersion('/admin/files/myfile.txt', 5, $view); } - public function testSetVersionWithoutFileInfo() { - $view = $this->getMockBuilder('\\OC\\Files\\View') + public function testSetVersionWithoutFileInfo(): void { + $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor()->getMock(); $view->expects($this->once()) ->method('getFileInfo') ->with('/admin/files/myfile.txt') ->willReturn(false); + /** @var View $view */ $this->instance->setVersion('/admin/files/myfile.txt', 5, $view); } + public function testBackupUserKeys(): void { + $this->keyStorageMock->expects($this->once())->method('backupUserKeys') + ->with('OC_DEFAULT_MODULE', 'test', 'user1'); + $this->instance->backupUserKeys('test', 'user1'); + } } diff --git a/apps/encryption/tests/Listeners/UserEventsListenersTest.php b/apps/encryption/tests/Listeners/UserEventsListenersTest.php new file mode 100644 index 00000000000..cb31523f105 --- /dev/null +++ b/apps/encryption/tests/Listeners/UserEventsListenersTest.php @@ -0,0 +1,258 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Encryption\Tests\Listeners; + +use OC\Core\Events\BeforePasswordResetEvent; +use OC\Core\Events\PasswordResetEvent; +use OC\Files\SetupManager; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Listeners\UserEventsListener; +use OCA\Encryption\Services\PassphraseService; +use OCA\Encryption\Session; +use OCA\Encryption\Users\Setup; +use OCA\Encryption\Util; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\User\Events\BeforePasswordUpdatedEvent; +use OCP\User\Events\PasswordUpdatedEvent; +use OCP\User\Events\UserCreatedEvent; +use OCP\User\Events\UserDeletedEvent; +use OCP\User\Events\UserLoggedInEvent; +use OCP\User\Events\UserLoggedOutEvent; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +/** + * @group DB + */ +class UserEventsListenersTest extends TestCase { + + protected Util&MockObject $util; + protected Setup&MockObject $userSetup; + protected Session&MockObject $session; + protected KeyManager&MockObject $keyManager; + protected IUserManager&MockObject $userManager; + protected IUserSession&MockObject $userSession; + protected SetupManager&MockObject $setupManager; + protected PassphraseService&MockObject $passphraseService; + + protected UserEventsListener $instance; + + public function setUp(): void { + parent::setUp(); + + $this->util = $this->createMock(Util::class); + $this->userSetup = $this->createMock(Setup::class); + $this->session = $this->createMock(Session::class); + $this->keyManager = $this->createMock(KeyManager::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->setupManager = $this->createMock(SetupManager::class); + $this->passphraseService = $this->createMock(PassphraseService::class); + + $this->instance = new UserEventsListener( + $this->util, + $this->userSetup, + $this->session, + $this->keyManager, + $this->userManager, + $this->userSession, + $this->setupManager, + $this->passphraseService, + ); + } + + public function testLogin(): void { + $this->userSetup->expects(self::once()) + ->method('setupUser') + ->willReturn(true); + + $this->keyManager->expects(self::once()) + ->method('init') + ->with('testUser', 'password'); + + $this->util->method('isMasterKeyEnabled')->willReturn(false); + + $user = $this->createMock(IUser::class); + $user->expects(self::any()) + ->method('getUID') + ->willReturn('testUser'); + $event = $this->createMock(UserLoggedInEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + + $this->instance->handle($event); + } + + public function testLoginMasterKey(): void { + $this->util->method('isMasterKeyEnabled')->willReturn(true); + + $this->userSetup->expects(self::never()) + ->method('setupUser'); + + $this->keyManager->expects(self::once()) + ->method('init') + ->with('testUser', 'password'); + + $user = $this->createMock(IUser::class); + $user->expects(self::any()) + ->method('getUID') + ->willReturn('testUser'); + + $event = $this->createMock(UserLoggedInEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + + $this->instance->handle($event); + } + + public function testLogout(): void { + $this->session->expects(self::once()) + ->method('clear'); + + $event = $this->createMock(UserLoggedOutEvent::class); + $this->instance->handle($event); + } + + public function testUserCreated(): void { + $this->userSetup->expects(self::once()) + ->method('setupUser') + ->with('testUser', 'password'); + + $event = $this->createMock(UserCreatedEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUid') + ->willReturn('testUser'); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + + $this->instance->handle($event); + } + + public function testUserDeleted(): void { + $this->keyManager->expects(self::once()) + ->method('deletePublicKey') + ->with('testUser'); + + $event = $this->createMock(UserDeletedEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUid') + ->willReturn('testUser'); + $this->instance->handle($event); + } + + public function testBeforePasswordUpdated(): void { + $this->passphraseService->expects(self::never()) + ->method('setPassphraseForUser'); + + $user = $this->createMock(IUser::class); + $user->expects(self::atLeastOnce()) + ->method('canChangePassword') + ->willReturn(true); + + $event = $this->createMock(BeforePasswordUpdatedEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + $this->instance->handle($event); + } + + public function testBeforePasswordUpdated_CannotChangePassword(): void { + $this->passphraseService->expects(self::once()) + ->method('setPassphraseForUser') + ->with('testUser', 'password'); + + $user = $this->createMock(IUser::class); + $user->expects(self::atLeastOnce()) + ->method('getUID') + ->willReturn('testUser'); + $user->expects(self::atLeastOnce()) + ->method('canChangePassword') + ->willReturn(false); + + $event = $this->createMock(BeforePasswordUpdatedEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + $this->instance->handle($event); + } + + public function testPasswordUpdated(): void { + $this->passphraseService->expects(self::once()) + ->method('setPassphraseForUser') + ->with('testUser', 'password'); + + $event = $this->createMock(PasswordUpdatedEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUid') + ->willReturn('testUser'); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + + $this->instance->handle($event); + } + + public function testBeforePasswordReset(): void { + $this->passphraseService->expects(self::once()) + ->method('setProcessingReset') + ->with('testUser'); + + $event = $this->createMock(BeforePasswordResetEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUid') + ->willReturn('testUser'); + $this->instance->handle($event); + } + + public function testPasswordReset(): void { + // backup required + $this->keyManager->expects(self::once()) + ->method('backupUserKeys') + ->with('passwordReset', 'testUser'); + // delete old keys + $this->keyManager->expects(self::once()) + ->method('deleteUserKeys') + ->with('testUser'); + // create new keys + $this->userSetup->expects(self::once()) + ->method('setupUser') + ->with('testUser', 'password'); + // reset ends + $this->passphraseService->expects(self::once()) + ->method('setProcessingReset') + ->with('testUser', false); + + $event = $this->createMock(PasswordResetEvent::class); + $event->expects(self::atLeastOnce()) + ->method('getUid') + ->willReturn('testUser'); + $event->expects(self::atLeastOnce()) + ->method('getPassword') + ->willReturn('password'); + $this->instance->handle($event); + } + +} diff --git a/apps/encryption/tests/PassphraseServiceTest.php b/apps/encryption/tests/PassphraseServiceTest.php new file mode 100644 index 00000000000..c2dc9d8173c --- /dev/null +++ b/apps/encryption/tests/PassphraseServiceTest.php @@ -0,0 +1,196 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Encryption\Tests; + +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Recovery; +use OCA\Encryption\Services\PassphraseService; +use OCA\Encryption\Session; +use OCA\Encryption\Util; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +/** + * @group DB + */ +class PassphraseServiceTest extends TestCase { + + protected Util&MockObject $util; + protected Crypt&MockObject $crypt; + protected Session&MockObject $session; + protected Recovery&MockObject $recovery; + protected KeyManager&MockObject $keyManager; + protected IUserManager&MockObject $userManager; + protected IUserSession&MockObject $userSession; + + protected PassphraseService $instance; + + public function setUp(): void { + parent::setUp(); + + $this->util = $this->createMock(Util::class); + $this->crypt = $this->createMock(Crypt::class); + $this->session = $this->createMock(Session::class); + $this->recovery = $this->createMock(Recovery::class); + $this->keyManager = $this->createMock(KeyManager::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->userSession = $this->createMock(IUserSession::class); + + $this->instance = new PassphraseService( + $this->util, + $this->crypt, + $this->session, + $this->recovery, + $this->keyManager, + $this->createMock(LoggerInterface::class), + $this->userManager, + $this->userSession, + ); + } + + public function testSetProcessingReset(): void { + $this->instance->setProcessingReset('userId'); + $this->assertEquals(['userId' => true], $this->invokePrivate($this->instance, 'passwordResetUsers')); + } + + public function testUnsetProcessingReset(): void { + $this->instance->setProcessingReset('userId'); + $this->assertEquals(['userId' => true], $this->invokePrivate($this->instance, 'passwordResetUsers')); + $this->instance->setProcessingReset('userId', false); + $this->assertEquals([], $this->invokePrivate($this->instance, 'passwordResetUsers')); + } + + /** + * Check that the passphrase setting skips if a reset is processed + */ + public function testSetPassphraseResetUserMode(): void { + $this->session->expects(self::never()) + ->method('getPrivateKey'); + $this->keyManager->expects(self::never()) + ->method('setPrivateKey'); + + $this->instance->setProcessingReset('userId'); + $this->assertTrue($this->instance->setPassphraseForUser('userId', 'password')); + } + + public function testSetPassphrase_currentUser() { + $instance = $this->getMockBuilder(PassphraseService::class) + ->onlyMethods(['initMountPoints']) + ->setConstructorArgs([ + $this->util, + $this->crypt, + $this->session, + $this->recovery, + $this->keyManager, + $this->createMock(LoggerInterface::class), + $this->userManager, + $this->userSession, + ]) + ->getMock(); + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('testUser'); + $this->userSession->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $this->userManager->expects(self::atLeastOnce()) + ->method('get') + ->with('testUser') + ->willReturn($user); + $this->session->expects(self::any()) + ->method('getPrivateKey') + ->willReturn('private-key'); + $this->crypt->expects(self::any()) + ->method('encryptPrivateKey') + ->with('private-key') + ->willReturn('encrypted-key'); + $this->crypt->expects(self::any()) + ->method('generateHeader') + ->willReturn('crypt-header: '); + + $this->keyManager->expects(self::atLeastOnce()) + ->method('setPrivateKey') + ->with('testUser', 'crypt-header: encrypted-key'); + + $this->assertTrue($instance->setPassphraseForUser('testUser', 'password')); + } + + public function testSetPassphrase_currentUserFails() { + $instance = $this->getMockBuilder(PassphraseService::class) + ->onlyMethods(['initMountPoints']) + ->setConstructorArgs([ + $this->util, + $this->crypt, + $this->session, + $this->recovery, + $this->keyManager, + $this->createMock(LoggerInterface::class), + $this->userManager, + $this->userSession, + ]) + ->getMock(); + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('testUser'); + $this->userManager->expects(self::atLeastOnce()) + ->method('get') + ->with('testUser') + ->willReturn($user); + $this->userSession->expects(self::atLeastOnce()) + ->method('getUser') + ->willReturn($user); + $this->session->expects(self::any()) + ->method('getPrivateKey') + ->willReturn('private-key'); + $this->crypt->expects(self::any()) + ->method('encryptPrivateKey') + ->with('private-key') + ->willReturn(false); + + $this->keyManager->expects(self::never()) + ->method('setPrivateKey'); + + $this->assertFalse($instance->setPassphraseForUser('testUser', 'password')); + } + + public function testSetPassphrase_currentUserNotExists() { + $instance = $this->getMockBuilder(PassphraseService::class) + ->onlyMethods(['initMountPoints']) + ->setConstructorArgs([ + $this->util, + $this->crypt, + $this->session, + $this->recovery, + $this->keyManager, + $this->createMock(LoggerInterface::class), + $this->userManager, + $this->userSession, + ]) + ->getMock(); + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('testUser'); + $this->userManager->expects(self::atLeastOnce()) + ->method('get') + ->with('testUser') + ->willReturn(null); + $this->userSession->expects(self::never()) + ->method('getUser'); + $this->keyManager->expects(self::never()) + ->method('setPrivateKey'); + + $this->assertFalse($instance->setPassphraseForUser('testUser', 'password')); + } + +} diff --git a/apps/encryption/tests/lib/RecoveryTest.php b/apps/encryption/tests/RecoveryTest.php index 68c21c80b34..0627724a856 100644 --- a/apps/encryption/tests/lib/RecoveryTest.php +++ b/apps/encryption/tests/RecoveryTest.php @@ -1,58 +1,53 @@ <?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Lukas Reschke <lukas@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ namespace OCA\Encryption\Tests; - +use OC\Files\View; +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; use OCA\Encryption\Recovery; +use OCP\Encryption\IFile; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class RecoveryTest extends TestCase { private static $tempStorage = []; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var IFile|\PHPUnit\Framework\MockObject\MockObject */ private $fileMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var View|\PHPUnit\Framework\MockObject\MockObject */ private $viewMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ private $userSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject|IUser + */ + private $user; + /** + * @var KeyManager|\PHPUnit\Framework\MockObject\MockObject */ private $keyManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ private $configMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Crypt|\PHPUnit\Framework\MockObject\MockObject */ private $cryptMock; /** @@ -60,7 +55,7 @@ class RecoveryTest extends TestCase { */ private $instance; - public function testEnableAdminRecoverySuccessful() { + public function testEnableAdminRecoverySuccessful(): void { $this->keyManagerMock->expects($this->exactly(2)) ->method('recoveryKeyExists') ->willReturnOnConsecutiveCalls(false, true); @@ -87,7 +82,7 @@ class RecoveryTest extends TestCase { $this->assertTrue($this->instance->enableAdminRecovery('password')); } - public function testEnableAdminRecoveryCouldNotCheckPassword() { + public function testEnableAdminRecoveryCouldNotCheckPassword(): void { $this->keyManagerMock->expects($this->exactly(2)) ->method('recoveryKeyExists') ->willReturnOnConsecutiveCalls(false, true); @@ -95,8 +90,8 @@ class RecoveryTest extends TestCase { $this->cryptMock->expects($this->once()) ->method('createKeyPair') ->willReturn([ - 'publicKey' => 'privateKey', - 'privateKey' => 'publicKey', + 'publicKey' => 'privateKey', + 'privateKey' => 'publicKey', ]); $this->keyManagerMock->expects($this->once()) @@ -114,7 +109,7 @@ class RecoveryTest extends TestCase { $this->assertFalse($this->instance->enableAdminRecovery('password')); } - public function testEnableAdminRecoveryCouldNotCreateKey() { + public function testEnableAdminRecoveryCouldNotCreateKey(): void { $this->keyManagerMock->expects($this->once()) ->method('recoveryKeyExists') ->willReturn(false); @@ -126,7 +121,7 @@ class RecoveryTest extends TestCase { $this->assertFalse($this->instance->enableAdminRecovery('password')); } - public function testChangeRecoveryKeyPasswordSuccessful() { + public function testChangeRecoveryKeyPasswordSuccessful(): void { $this->assertFalse($this->instance->changeRecoveryKeyPassword('password', 'passwordOld')); @@ -144,7 +139,7 @@ class RecoveryTest extends TestCase { 'passwordOld')); } - public function testChangeRecoveryKeyPasswordCouldNotDecryptPrivateRecoveryKey() { + public function testChangeRecoveryKeyPasswordCouldNotDecryptPrivateRecoveryKey(): void { $this->assertFalse($this->instance->changeRecoveryKeyPassword('password', 'passwordOld')); $this->keyManagerMock->expects($this->once()) @@ -152,13 +147,12 @@ class RecoveryTest extends TestCase { $this->cryptMock->expects($this->once()) ->method('decryptPrivateKey') - ->will($this->returnValue(false)); + ->willReturn(false); $this->assertFalse($this->instance->changeRecoveryKeyPassword('password', 'passwordOld')); } - public function testDisableAdminRecovery() { - + public function testDisableAdminRecovery(): void { $this->keyManagerMock->expects($this->exactly(2)) ->method('checkRecoveryPassword') ->willReturnOnConsecutiveCalls(true, false); @@ -170,8 +164,7 @@ class RecoveryTest extends TestCase { $this->assertFalse($this->instance->disableAdminRecovery('password')); } - public function testIsRecoveryEnabledForUser() { - + public function testIsRecoveryEnabledForUser(): void { $this->configMock->expects($this->exactly(2)) ->method('getUserValue') ->willReturnOnConsecutiveCalls('1', '0'); @@ -180,13 +173,13 @@ class RecoveryTest extends TestCase { $this->assertFalse($this->instance->isRecoveryEnabledForUser('admin')); } - public function testIsRecoveryKeyEnabled() { + public function testIsRecoveryKeyEnabled(): void { $this->assertFalse($this->instance->isRecoveryKeyEnabled()); self::$tempStorage['recoveryAdminEnabled'] = '1'; $this->assertTrue($this->instance->isRecoveryKeyEnabled()); } - public function testSetRecoveryFolderForUser() { + public function testSetRecoveryFolderForUser(): void { $this->viewMock->expects($this->exactly(2)) ->method('getDirectoryContent') ->willReturn([]); @@ -194,17 +187,19 @@ class RecoveryTest extends TestCase { $this->assertTrue($this->instance->setRecoveryForUser('1')); } - public function testRecoverUserFiles() { + public function testRecoverUserFiles(): void { $this->viewMock->expects($this->once()) ->method('getDirectoryContent') ->willReturn([]); $this->cryptMock->expects($this->once()) - ->method('decryptPrivateKey'); - $this->assertNull($this->instance->recoverUsersFiles('password', 'admin')); + ->method('decryptPrivateKey') + ->willReturn('privateKey'); + $this->instance->recoverUsersFiles('password', 'admin'); + $this->addToAssertionCount(1); } - public function testRecoverFile() { + public function testRecoverFile(): void { $this->keyManagerMock->expects($this->once()) ->method('getEncryptedFileKey') ->willReturn(true); @@ -214,8 +209,8 @@ class RecoveryTest extends TestCase { ->willReturn(true); $this->cryptMock->expects($this->once()) - ->method('multiKeyDecrypt') - ->willReturn(true); + ->method('multiKeyDecryptLegacy') + ->willReturn('multiKeyDecryptLegacyResult'); $this->fileMock->expects($this->once()) ->method('getAccessList') @@ -232,60 +227,53 @@ class RecoveryTest extends TestCase { $this->cryptMock->expects($this->once()) - ->method('multiKeyEncrypt'); + ->method('multiKeyEncrypt') + ->willReturn(['admin' => 'shareKey']); $this->keyManagerMock->expects($this->once()) - ->method('setAllFileKeys'); + ->method('deleteLegacyFileKey'); + $this->keyManagerMock->expects($this->once()) + ->method('setShareKey'); $this->assertNull(self::invokePrivate($this->instance, 'recoverFile', ['/', 'testkey', 'admin'])); } - protected function setUp() { + protected function setUp(): void { parent::setUp(); + $this->user = $this->createMock(IUser::class); + $this->user->expects($this->any()) + ->method('getUID') + ->willReturn('admin'); - $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'getUID', - 'login', - 'logout', - 'setUser', - 'getUser' - ]) - ->getMock(); - - $this->userSessionMock->expects($this->any())->method('getUID')->will($this->returnValue('admin')); - + $this->userSessionMock = $this->createMock(IUserSession::class); + $this->userSessionMock->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); $this->userSessionMock->expects($this->any()) - ->method($this->anything()) - ->will($this->returnSelf()); + ->method('isLoggedIn') + ->willReturn(true); - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')->disableOriginalConstructor()->getMock(); - $randomMock = $this->getMock('OCP\Security\ISecureRandom'); - $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')->disableOriginalConstructor()->getMock(); - $this->configMock = $this->getMock('OCP\IConfig'); - $keyStorageMock = $this->getMock('OCP\Encryption\Keys\IStorage'); - $this->fileMock = $this->getMock('OCP\Encryption\IFile'); - $this->viewMock = $this->getMock('OC\Files\View'); + $this->cryptMock = $this->getMockBuilder(Crypt::class)->disableOriginalConstructor()->getMock(); + $this->keyManagerMock = $this->getMockBuilder(KeyManager::class)->disableOriginalConstructor()->getMock(); + $this->configMock = $this->createMock(IConfig::class); + $this->fileMock = $this->createMock(IFile::class); + $this->viewMock = $this->createMock(View::class); $this->configMock->expects($this->any()) ->method('setAppValue') - ->will($this->returnCallback([$this, 'setValueTester'])); + ->willReturnCallback([$this, 'setValueTester']); $this->configMock->expects($this->any()) ->method('getAppValue') - ->will($this->returnCallback([$this, 'getValueTester'])); + ->willReturnCallback([$this, 'getValueTester']); $this->instance = new Recovery($this->userSessionMock, $this->cryptMock, - $randomMock, $this->keyManagerMock, $this->configMock, - $keyStorageMock, $this->fileMock, $this->viewMock); } @@ -318,6 +306,4 @@ class RecoveryTest extends TestCase { } return null; } - - } diff --git a/apps/encryption/tests/lib/SessionTest.php b/apps/encryption/tests/SessionTest.php index f7a8a0369bb..986502749c8 100644 --- a/apps/encryption/tests/lib/SessionTest.php +++ b/apps/encryption/tests/SessionTest.php @@ -1,62 +1,45 @@ <?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ namespace OCA\Encryption\Tests; - +use OCA\Encryption\Exceptions\PrivateKeyMissingException; use OCA\Encryption\Session; +use OCP\ISession; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SessionTest extends TestCase { private static $tempStorage = []; - /** - * @var Session - */ - private $instance; - private $sessionMock; - /** - * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException - * @expectedExceptionMessage Private Key missing for user: please try to log-out and log-in again - */ - public function testThatGetPrivateKeyThrowsExceptionWhenNotSet() { + protected Session $instance; + protected ISession&MockObject $sessionMock; + + public function testThatGetPrivateKeyThrowsExceptionWhenNotSet(): void { + $this->expectException(PrivateKeyMissingException::class); + $this->expectExceptionMessage('Private Key missing for user: please try to log-out and log-in again'); + $this->instance->getPrivateKey(); } /** * @depends testThatGetPrivateKeyThrowsExceptionWhenNotSet */ - public function testSetAndGetPrivateKey() { + public function testSetAndGetPrivateKey(): void { $this->instance->setPrivateKey('dummyPrivateKey'); $this->assertEquals('dummyPrivateKey', $this->instance->getPrivateKey()); - } /** * @depends testSetAndGetPrivateKey */ - public function testIsPrivateKeySet() { + public function testIsPrivateKeySet(): void { $this->instance->setPrivateKey('dummyPrivateKey'); $this->assertTrue($this->instance->isPrivateKeySet()); @@ -67,55 +50,57 @@ class SessionTest extends TestCase { self::$tempStorage['privateKey'] = 'dummyPrivateKey'; } - public function testDecryptAllModeActivated() { + public function testDecryptAllModeActivated(): void { $this->instance->prepareDecryptAll('user1', 'usersKey'); $this->assertTrue($this->instance->decryptAllModeActivated()); $this->assertSame('user1', $this->instance->getDecryptAllUid()); $this->assertSame('usersKey', $this->instance->getDecryptAllKey()); } - public function testDecryptAllModeDeactivated() { + public function testDecryptAllModeDeactivated(): void { $this->assertFalse($this->instance->decryptAllModeActivated()); } /** - * @expectedException \Exception * @expectExceptionMessage 'Please activate decrypt all mode first' */ - public function testGetDecryptAllUidException() { + public function testGetDecryptAllUidException(): void { + $this->expectException(\Exception::class); + $this->instance->getDecryptAllUid(); } /** - * @expectedException \Exception * @expectExceptionMessage 'No uid found while in decrypt all mode' */ - public function testGetDecryptAllUidException2() { + public function testGetDecryptAllUidException2(): void { + $this->expectException(\Exception::class); + $this->instance->prepareDecryptAll(null, 'key'); $this->instance->getDecryptAllUid(); } /** - * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException * @expectExceptionMessage 'Please activate decrypt all mode first' */ - public function testGetDecryptAllKeyException() { + public function testGetDecryptAllKeyException(): void { + $this->expectException(PrivateKeyMissingException::class); + $this->instance->getDecryptAllKey(); } /** - * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException * @expectExceptionMessage 'No key found while in decrypt all mode' */ - public function testGetDecryptAllKeyException2() { + public function testGetDecryptAllKeyException2(): void { + $this->expectException(PrivateKeyMissingException::class); + $this->instance->prepareDecryptAll('user', null); $this->instance->getDecryptAllKey(); } - /** - * - */ - public function testSetAndGetStatusWillSetAndReturn() { + + public function testSetAndGetStatusWillSetAndReturn(): void { // Check if get status will return 0 if it has not been set before $this->assertEquals(0, $this->instance->getStatus()); @@ -130,6 +115,33 @@ class SessionTest extends TestCase { } /** + * + * @param int $status + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsReady')] + public function testIsReady($status, $expected): void { + /** @var Session&MockObject $instance */ + $instance = $this->getMockBuilder(Session::class) + ->setConstructorArgs([$this->sessionMock]) + ->onlyMethods(['getStatus']) + ->getMock(); + + $instance->expects($this->once())->method('getStatus') + ->willReturn($status); + + $this->assertSame($expected, $instance->isReady()); + } + + public static function dataTestIsReady(): array { + return [ + [Session::INIT_SUCCESSFUL, true], + [Session::INIT_EXECUTED, false], + [Session::NOT_INITIALIZED, false], + ]; + } + + /** * @param $key * @param $value */ @@ -155,10 +167,8 @@ class SessionTest extends TestCase { return null; } - /** - * - */ - public function testClearWillRemoveValues() { + + public function testClearWillRemoveValues(): void { $this->instance->setPrivateKey('privateKey'); $this->instance->setStatus('initStatus'); $this->instance->prepareDecryptAll('user', 'key'); @@ -167,30 +177,28 @@ class SessionTest extends TestCase { $this->assertEmpty(self::$tempStorage); } - /** - * - */ - protected function setUp() { + + protected function setUp(): void { parent::setUp(); - $this->sessionMock = $this->getMock('OCP\ISession'); + $this->sessionMock = $this->createMock(ISession::class); $this->sessionMock->expects($this->any()) ->method('set') - ->will($this->returnCallback([$this, "setValueTester"])); + ->willReturnCallback([$this, 'setValueTester']); $this->sessionMock->expects($this->any()) ->method('get') - ->will($this->returnCallback([$this, "getValueTester"])); + ->willReturnCallback([$this, 'getValueTester']); $this->sessionMock->expects($this->any()) ->method('remove') - ->will($this->returnCallback([$this, "removeValueTester"])); + ->willReturnCallback([$this, 'removeValueTester']); $this->instance = new Session($this->sessionMock); } - protected function tearDown() { + protected function tearDown(): void { self::$tempStorage = []; parent::tearDown(); } diff --git a/apps/encryption/tests/Settings/AdminTest.php b/apps/encryption/tests/Settings/AdminTest.php new file mode 100644 index 00000000000..8355cdf6729 --- /dev/null +++ b/apps/encryption/tests/Settings/AdminTest.php @@ -0,0 +1,82 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Encryption\Tests\Settings; + +use OCA\Encryption\Settings\Admin; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\ISession; +use OCP\IUserManager; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class AdminTest extends TestCase { + + protected Admin $admin; + + protected IL10N&MockObject $l; + protected LoggerInterface&MockObject $logger; + protected IUserSession&MockObject $userSession; + protected IConfig&MockObject $config; + protected IUserManager&MockObject $userManager; + protected ISession&MockObject $session; + + protected function setUp(): void { + parent::setUp(); + + $this->l = $this->getMockBuilder(IL10N::class)->getMock(); + $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $this->userSession = $this->getMockBuilder(IUserSession::class)->getMock(); + $this->config = $this->getMockBuilder(IConfig::class)->getMock(); + $this->userManager = $this->getMockBuilder(IUserManager::class)->getMock(); + $this->session = $this->getMockBuilder(ISession::class)->getMock(); + + $this->admin = new Admin( + $this->l, + $this->logger, + $this->userSession, + $this->config, + $this->userManager, + $this->session + ); + } + + public function testGetForm(): void { + $this->config + ->method('getAppValue') + ->willReturnCallback(function ($app, $key, $default) { + if ($app === 'encryption' && $key === 'recoveryAdminEnabled' && $default === '0') { + return '1'; + } + if ($app === 'encryption' && $key === 'encryptHomeStorage' && $default === '1') { + return '1'; + } + return $default; + }); + $params = [ + 'recoveryEnabled' => '1', + 'initStatus' => '0', + 'encryptHomeStorage' => true, + 'masterKeyEnabled' => true + ]; + $expected = new TemplateResponse('encryption', 'settings-admin', $params, ''); + $this->assertEquals($expected, $this->admin->getForm()); + } + + public function testGetSection(): void { + $this->assertSame('security', $this->admin->getSection()); + } + + public function testGetPriority(): void { + $this->assertSame(11, $this->admin->getPriority()); + } +} diff --git a/apps/encryption/tests/Users/SetupTest.php b/apps/encryption/tests/Users/SetupTest.php new file mode 100644 index 00000000000..6b2b8b4cad5 --- /dev/null +++ b/apps/encryption/tests/Users/SetupTest.php @@ -0,0 +1,77 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests\Users; + +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Users\Setup; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class SetupTest extends TestCase { + + protected Setup $instance; + + protected KeyManager&MockObject $keyManagerMock; + protected Crypt&MockObject $cryptMock; + + protected function setUp(): void { + parent::setUp(); + $this->cryptMock = $this->getMockBuilder(Crypt::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->keyManagerMock = $this->getMockBuilder(KeyManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->instance = new Setup( + $this->cryptMock, + $this->keyManagerMock); + } + + + public function testSetupSystem(): void { + $this->keyManagerMock->expects($this->once())->method('validateShareKey'); + $this->keyManagerMock->expects($this->once())->method('validateMasterKey'); + + $this->instance->setupSystem(); + } + + /** + * + * @param bool $hasKeys + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSetupUser')] + public function testSetupUser($hasKeys, $expected): void { + $this->keyManagerMock->expects($this->once())->method('userHasKeys') + ->with('uid')->willReturn($hasKeys); + + if ($hasKeys) { + $this->keyManagerMock->expects($this->never())->method('storeKeyPair'); + } else { + $this->cryptMock->expects($this->once())->method('createKeyPair')->willReturn(['publicKey' => 'publicKey', 'privateKey' => 'privateKey']); + $this->keyManagerMock->expects($this->once())->method('storeKeyPair') + ->with('uid', 'password', ['publicKey' => 'publicKey', 'privateKey' => 'privateKey'])->willReturn(true); + } + + $this->assertSame($expected, + $this->instance->setupUser('uid', 'password') + ); + } + + public static function dataTestSetupUser(): array { + return [ + [true, true], + [false, true] + ]; + } +} diff --git a/apps/encryption/tests/UtilTest.php b/apps/encryption/tests/UtilTest.php new file mode 100644 index 00000000000..41860a44ffb --- /dev/null +++ b/apps/encryption/tests/UtilTest.php @@ -0,0 +1,189 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Encryption\Tests; + +use OC\Files\View; +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\Util; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Storage\IStorage; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class UtilTest extends TestCase { + + protected Util $instance; + protected static $tempStorage = []; + + protected IConfig&MockObject $configMock; + protected View&MockObject $filesMock; + protected IUserManager&MockObject $userManagerMock; + protected IMountPoint&MockObject $mountMock; + + public function testSetRecoveryForUser(): void { + $this->instance->setRecoveryForUser('1'); + $this->assertArrayHasKey('recoveryEnabled', self::$tempStorage); + } + + public function testIsRecoveryEnabledForUser(): void { + $this->assertTrue($this->instance->isRecoveryEnabledForUser('admin')); + + // Assert recovery will return default value if not set + unset(self::$tempStorage['recoveryEnabled']); + $this->assertEquals(0, $this->instance->isRecoveryEnabledForUser('admin')); + } + + public function testUserHasFiles(): void { + $this->filesMock->expects($this->once()) + ->method('file_exists') + ->willReturn(true); + + $this->assertTrue($this->instance->userHasFiles('admin')); + } + + protected function setUp(): void { + parent::setUp(); + $this->mountMock = $this->createMock(IMountPoint::class); + $this->filesMock = $this->createMock(View::class); + $this->userManagerMock = $this->createMock(IUserManager::class); + + /** @var Crypt $cryptMock */ + $cryptMock = $this->getMockBuilder(Crypt::class) + ->disableOriginalConstructor() + ->getMock(); + + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('admin'); + + /** @var IUserSession|MockObject $userSessionMock */ + $userSessionMock = $this->createMock(IUserSession::class); + $userSessionMock->expects($this->any()) + ->method('getUser') + ->willReturn($user); + $userSessionMock->expects($this->any()) + ->method('isLoggedIn') + ->willReturn(true); + + $this->configMock = $this->createMock(IConfig::class); + + $this->configMock->expects($this->any()) + ->method('getUserValue') + ->willReturnCallback([$this, 'getValueTester']); + + $this->configMock->expects($this->any()) + ->method('setUserValue') + ->willReturnCallback([$this, 'setValueTester']); + + $this->instance = new Util($this->filesMock, $cryptMock, $userSessionMock, $this->configMock, $this->userManagerMock); + } + + /** + * @param $userId + * @param $app + * @param $key + * @param $value + */ + public function setValueTester($userId, $app, $key, $value) { + self::$tempStorage[$key] = $value; + } + + /** + * @param $userId + * @param $app + * @param $key + * @param $default + * @return mixed + */ + public function getValueTester($userId, $app, $key, $default) { + if (!empty(self::$tempStorage[$key])) { + return self::$tempStorage[$key]; + } + return $default ?: null; + } + + /** + * + * @param string $value + * @param bool $expect + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsMasterKeyEnabled')] + public function testIsMasterKeyEnabled($value, $expect): void { + $this->configMock->expects($this->once())->method('getAppValue') + ->with('encryption', 'useMasterKey', '1')->willReturn($value); + $this->assertSame($expect, + $this->instance->isMasterKeyEnabled() + ); + } + + public static function dataTestIsMasterKeyEnabled(): array { + return [ + ['0', false], + ['1', true] + ]; + } + + /** + * @param string $returnValue return value from getAppValue() + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncryptHomeStorage')] + public function testShouldEncryptHomeStorage($returnValue, $expected): void { + $this->configMock->expects($this->once())->method('getAppValue') + ->with('encryption', 'encryptHomeStorage', '1') + ->willReturn($returnValue); + + $this->assertSame($expected, + $this->instance->shouldEncryptHomeStorage()); + } + + public static function dataTestShouldEncryptHomeStorage(): array { + return [ + ['1', true], + ['0', false] + ]; + } + + /** + * @param $value + * @param $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSetEncryptHomeStorage')] + public function testSetEncryptHomeStorage($value, $expected): void { + $this->configMock->expects($this->once())->method('setAppValue') + ->with('encryption', 'encryptHomeStorage', $expected); + $this->instance->setEncryptHomeStorage($value); + } + + public static function dataTestSetEncryptHomeStorage(): array { + return [ + [true, '1'], + [false, '0'] + ]; + } + + public function testGetStorage(): void { + $return = $this->getMockBuilder(IStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $path = '/foo/bar.txt'; + $this->filesMock->expects($this->once())->method('getMount')->with($path) + ->willReturn($this->mountMock); + $this->mountMock->expects($this->once())->method('getStorage')->willReturn($return); + + $this->assertEquals($return, $this->instance->getStorage($path)); + } +} diff --git a/apps/encryption/tests/command/testenablemasterkey.php b/apps/encryption/tests/command/testenablemasterkey.php deleted file mode 100644 index e408a7c2b14..00000000000 --- a/apps/encryption/tests/command/testenablemasterkey.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests\Command; - - -use OCA\Encryption\Command\EnableMasterKey; -use Test\TestCase; - -class TestEnableMasterKey extends TestCase { - - /** @var EnableMasterKey */ - protected $enableMasterKey; - - /** @var Util | \PHPUnit_Framework_MockObject_MockObject */ - protected $util; - - /** @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject */ - protected $config; - - /** @var \Symfony\Component\Console\Helper\QuestionHelper | \PHPUnit_Framework_MockObject_MockObject */ - protected $questionHelper; - - /** @var \Symfony\Component\Console\Output\OutputInterface | \PHPUnit_Framework_MockObject_MockObject */ - protected $output; - - /** @var \Symfony\Component\Console\Input\InputInterface | \PHPUnit_Framework_MockObject_MockObject */ - protected $input; - - public function setUp() { - parent::setUp(); - - $this->util = $this->getMockBuilder('OCA\Encryption\Util') - ->disableOriginalConstructor()->getMock(); - $this->config = $this->getMockBuilder('OCP\IConfig') - ->disableOriginalConstructor()->getMock(); - $this->questionHelper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor()->getMock(); - $this->output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface') - ->disableOriginalConstructor()->getMock(); - $this->input = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface') - ->disableOriginalConstructor()->getMock(); - - $this->enableMasterKey = new EnableMasterKey($this->util, $this->config, $this->questionHelper); - } - - /** - * @dataProvider dataTestExecute - * - * @param bool $isAlreadyEnabled - * @param string $answer - */ - public function testExecute($isAlreadyEnabled, $answer) { - - $this->util->expects($this->once())->method('isMasterKeyEnabled') - ->willReturn($isAlreadyEnabled); - - if ($isAlreadyEnabled) { - $this->output->expects($this->once())->method('writeln') - ->with('Master key already enabled'); - } else { - if ($answer === 'y') { - $this->questionHelper->expects($this->once())->method('ask')->willReturn(true); - $this->config->expects($this->once())->method('setAppValue') - ->with('encryption', 'useMasterKey', '1'); - } else { - $this->questionHelper->expects($this->once())->method('ask')->willReturn(false); - $this->config->expects($this->never())->method('setAppValue'); - - } - } - - $this->invokePrivate($this->enableMasterKey, 'execute', [$this->input, $this->output]); - } - - public function dataTestExecute() { - return [ - [true, ''], - [false, 'y'], - [false, 'n'], - [false, ''] - ]; - } -} diff --git a/apps/encryption/tests/controller/StatusControllerTest.php b/apps/encryption/tests/controller/StatusControllerTest.php deleted file mode 100644 index 3c937623b7a..00000000000 --- a/apps/encryption/tests/controller/StatusControllerTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests\Controller; - - -use OCA\Encryption\Controller\StatusController; -use OCA\Encryption\Session; -use Test\TestCase; - -class StatusControllerTest extends TestCase { - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $requestMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $l10nMock; - - /** @var \OCA\Encryption\Session | \PHPUnit_Framework_MockObject_MockObject */ - protected $sessionMock; - - /** @var StatusController */ - protected $controller; - - protected function setUp() { - - parent::setUp(); - - $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') - ->disableOriginalConstructor()->getMock(); - $this->requestMock = $this->getMock('OCP\IRequest'); - - $this->l10nMock = $this->getMockBuilder('OCP\IL10N') - ->disableOriginalConstructor()->getMock(); - $this->l10nMock->expects($this->any()) - ->method('t') - ->will($this->returnCallback(function($message) { - return $message; - })); - - $this->controller = new StatusController('encryptionTest', - $this->requestMock, - $this->l10nMock, - $this->sessionMock); - - } - - /** - * @dataProvider dataTestGetStatus - * - * @param string $status - * @param string $expectedStatus - */ - public function testGetStatus($status, $expectedStatus) { - $this->sessionMock->expects($this->once()) - ->method('getStatus')->willReturn($status); - $result = $this->controller->getStatus(); - $data = $result->getData(); - $this->assertSame($expectedStatus, $data['status']); - } - - public function dataTestGetStatus() { - return array( - array(Session::RUN_MIGRATION, 'interactionNeeded'), - array(Session::INIT_EXECUTED, 'interactionNeeded'), - array(Session::INIT_SUCCESSFUL, 'success'), - array(Session::NOT_INITIALIZED, 'interactionNeeded'), - array('unknown', 'error'), - ); - } -} diff --git a/apps/encryption/tests/hooks/UserHooksTest.php b/apps/encryption/tests/hooks/UserHooksTest.php deleted file mode 100644 index 08d1981266c..00000000000 --- a/apps/encryption/tests/hooks/UserHooksTest.php +++ /dev/null @@ -1,328 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - - -namespace OCA\Encryption\Tests\Hooks; - - -use OCA\Encryption\Crypto\Crypt; -use OCA\Encryption\Hooks\UserHooks; -use Test\TestCase; - -class UserHooksTest extends TestCase { - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $utilMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $recoveryMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $sessionMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $keyManagerMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $userManagerMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $userSetupMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $userSessionMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $cryptMock; - /** - * @var UserHooks - */ - private $instance; - - private $params = ['uid' => 'testUser', 'password' => 'password']; - - public function testLogin() { - $this->userSetupMock->expects($this->exactly(2)) - ->method('setupUser') - ->willReturnOnConsecutiveCalls(true, false); - - $this->keyManagerMock->expects($this->once()) - ->method('init') - ->with('testUser', 'password'); - - $this->assertNull($this->instance->login($this->params)); - $this->assertFalse($this->instance->login($this->params)); - } - - public function testLogout() { - $this->sessionMock->expects($this->once()) - ->method('clear'); - - $this->assertNull($this->instance->logout()); - } - - public function testPostCreateUser() { - $this->userSetupMock->expects($this->once()) - ->method('setupUser'); - - $this->assertNull($this->instance->postCreateUser($this->params)); - } - - public function testPostDeleteUser() { - $this->keyManagerMock->expects($this->once()) - ->method('deletePublicKey') - ->with('testUser'); - - $this->assertNull($this->instance->postDeleteUser($this->params)); - } - - /** - * @dataProvider dataTestPreSetPassphrase - */ - public function testPreSetPassphrase($canChange) { - - /** @var UserHooks | \PHPUnit_Framework_MockObject_MockObject $instance */ - $instance = $this->getMockBuilder('OCA\Encryption\Hooks\UserHooks') - ->setConstructorArgs( - [ - $this->keyManagerMock, - $this->userManagerMock, - $this->loggerMock, - $this->userSetupMock, - $this->userSessionMock, - $this->utilMock, - $this->sessionMock, - $this->cryptMock, - $this->recoveryMock - ] - ) - ->setMethods(['setPassphrase']) - ->getMock(); - - $userMock = $this->getMock('OCP\IUser'); - - $this->userManagerMock->expects($this->once()) - ->method('get') - ->with($this->params['uid']) - ->willReturn($userMock); - $userMock->expects($this->once()) - ->method('canChangePassword') - ->willReturn($canChange); - - if ($canChange) { - // in this case the password will be changed in the post hook - $instance->expects($this->never())->method('setPassphrase'); - } else { - // if user can't change the password we update the encryption - // key password already in the pre hook - $instance->expects($this->once()) - ->method('setPassphrase') - ->with($this->params); - } - - $instance->preSetPassphrase($this->params); - } - - public function dataTestPreSetPassphrase() { - return [ - [true], - [false] - ]; - } - - public function testSetPassphrase() { - $this->sessionMock->expects($this->exactly(4)) - ->method('getPrivateKey') - ->willReturnOnConsecutiveCalls(true, false); - - $this->cryptMock->expects($this->exactly(4)) - ->method('encryptPrivateKey') - ->willReturn(true); - - $this->cryptMock->expects($this->any()) - ->method('generateHeader') - ->willReturn(Crypt::HEADER_START . ':Cipher:test:' . Crypt::HEADER_END); - - $this->keyManagerMock->expects($this->exactly(4)) - ->method('setPrivateKey') - ->willReturnCallback(function ($user, $key) { - $header = substr($key, 0, strlen(Crypt::HEADER_START)); - $this->assertSame( - Crypt::HEADER_START, - $header, 'every encrypted file should start with a header'); - }); - - $this->assertNull($this->instance->setPassphrase($this->params)); - $this->params['recoveryPassword'] = 'password'; - - $this->recoveryMock->expects($this->exactly(3)) - ->method('isRecoveryEnabledForUser') - ->with('testUser') - ->willReturnOnConsecutiveCalls(true, false); - - - // Test first if statement - $this->assertNull($this->instance->setPassphrase($this->params)); - - // Test Second if conditional - $this->keyManagerMock->expects($this->exactly(2)) - ->method('userHasKeys') - ->with('testUser') - ->willReturn(true); - - $this->assertNull($this->instance->setPassphrase($this->params)); - - // Test third and final if condition - $this->utilMock->expects($this->once()) - ->method('userHasFiles') - ->with('testUser') - ->willReturn(false); - - $this->cryptMock->expects($this->once()) - ->method('createKeyPair'); - - $this->keyManagerMock->expects($this->once()) - ->method('setPrivateKey'); - - $this->recoveryMock->expects($this->once()) - ->method('recoverUsersFiles') - ->with('password', 'testUser'); - - $this->assertNull($this->instance->setPassphrase($this->params)); - } - - public function testSetPasswordNoUser() { - $this->sessionMock->expects($this->once()) - ->method('getPrivateKey') - ->willReturn(true); - - $userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->getMock(); - - $userSessionMock->expects($this->any())->method('getUser')->will($this->returnValue(null)); - - $this->recoveryMock->expects($this->once()) - ->method('isRecoveryEnabledForUser') - ->with('testUser') - ->willReturn(false); - - $userHooks = new UserHooks($this->keyManagerMock, - $this->userManagerMock, - $this->loggerMock, - $this->userSetupMock, - $userSessionMock, - $this->utilMock, - $this->sessionMock, - $this->cryptMock, - $this->recoveryMock - ); - - $this->assertNull($userHooks->setPassphrase($this->params)); - } - - public function testPostPasswordReset() { - $this->keyManagerMock->expects($this->once()) - ->method('replaceUserKeys') - ->with('testUser'); - - $this->userSetupMock->expects($this->once()) - ->method('setupServerSide') - ->with('testUser', 'password'); - - $this->assertNull($this->instance->postPasswordReset($this->params)); - } - - protected function setUp() { - parent::setUp(); - $this->loggerMock = $this->getMock('OCP\ILogger'); - $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') - ->disableOriginalConstructor() - ->getMock(); - $this->userManagerMock = $this->getMockBuilder('OCP\IUserManager') - ->disableOriginalConstructor() - ->getMock(); - $this->userSetupMock = $this->getMockBuilder('OCA\Encryption\Users\Setup') - ->disableOriginalConstructor() - ->getMock(); - - $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'getUID', - 'login', - 'logout', - 'setUser', - 'getUser', - 'canChangePassword' - ]) - ->getMock(); - - $this->userSessionMock->expects($this->any())->method('getUID')->will($this->returnValue('testUser')); - - $this->userSessionMock->expects($this->any()) - ->method($this->anything()) - ->will($this->returnSelf()); - - $utilMock = $this->getMockBuilder('OCA\Encryption\Util') - ->disableOriginalConstructor() - ->getMock(); - - $sessionMock = $this->getMockBuilder('OCA\Encryption\Session') - ->disableOriginalConstructor() - ->getMock(); - - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') - ->disableOriginalConstructor() - ->getMock(); - $recoveryMock = $this->getMockBuilder('OCA\Encryption\Recovery') - ->disableOriginalConstructor() - ->getMock(); - - $this->sessionMock = $sessionMock; - $this->recoveryMock = $recoveryMock; - $this->utilMock = $utilMock; - $this->instance = new UserHooks($this->keyManagerMock, - $this->userManagerMock, - $this->loggerMock, - $this->userSetupMock, - $this->userSessionMock, - $this->utilMock, - $this->sessionMock, - $this->cryptMock, - $this->recoveryMock - ); - - } - -} diff --git a/apps/encryption/tests/lib/HookManagerTest.php b/apps/encryption/tests/lib/HookManagerTest.php deleted file mode 100644 index d69674faec0..00000000000 --- a/apps/encryption/tests/lib/HookManagerTest.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php -/** - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests; - - -use OCA\Encryption\HookManager; -use Test\TestCase; - -class HookManagerTest extends TestCase { - - /** - * @var HookManager - */ - private static $instance; - - /** - * - */ - public function testRegisterHookWithArray() { - self::$instance->registerHook([ - $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock(), - $this->getMock('NotIHook') - ]); - - $hookInstances = self::invokePrivate(self::$instance, 'hookInstances'); - // Make sure our type checking works - $this->assertCount(2, $hookInstances); - } - - - /** - * - */ - public static function setUpBeforeClass() { - parent::setUpBeforeClass(); - // have to make instance static to preserve data between tests - self::$instance = new HookManager(); - - } - - /** - * - */ - public function testRegisterHooksWithInstance() { - $mock = $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock(); - self::$instance->registerHook($mock); - - $hookInstances = self::invokePrivate(self::$instance, 'hookInstances'); - $this->assertCount(3, $hookInstances); - - } - -} diff --git a/apps/encryption/tests/lib/MigrationTest.php b/apps/encryption/tests/lib/MigrationTest.php deleted file mode 100644 index 6edb8624e70..00000000000 --- a/apps/encryption/tests/lib/MigrationTest.php +++ /dev/null @@ -1,586 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Robin Appelman <icewind@owncloud.com> - * @author Roeland Jago Douma <rullzer@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Encryption\Tests; - -use OCA\Encryption\Migration; -use OCP\ILogger; - -class MigrationTest extends \Test\TestCase { - - const TEST_ENCRYPTION_MIGRATION_USER1='test_encryption_user1'; - const TEST_ENCRYPTION_MIGRATION_USER2='test_encryption_user2'; - const TEST_ENCRYPTION_MIGRATION_USER3='test_encryption_user3'; - - /** @var \OC\Files\View */ - private $view; - private $public_share_key_id = 'share_key_id'; - private $recovery_key_id = 'recovery_key_id'; - private $moduleId; - - /** @var PHPUnit_Framework_MockObject_MockObject | ILogger */ - private $logger; - - public static function setUpBeforeClass() { - parent::setUpBeforeClass(); - \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo'); - \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo'); - \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo'); - } - - public static function tearDownAfterClass() { - $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER1); - if ($user !== null) { $user->delete(); } - $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER2); - if ($user !== null) { $user->delete(); } - $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER3); - if ($user !== null) { $user->delete(); } - parent::tearDownAfterClass(); - } - - - public function setUp() { - $this->logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock(); - $this->view = new \OC\Files\View(); - $this->moduleId = \OCA\Encryption\Crypto\Encryption::ID; - } - - /** - * @param string $uid - */ - protected function createDummyShareKeys($uid) { - $this->loginAsUser($uid); - - $this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3'); - $this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2'); - $this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1'); - $this->view->mkdir($uid . '/files_encryption/keys/folder2/file.2.1'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); - if ($this->public_share_key_id) { - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . $this->public_share_key_id . '.shareKey' , 'data'); - } - if ($this->recovery_key_id) { - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . $this->recovery_key_id . '.shareKey' , 'data'); - } - } - - /** - * @param string $uid - */ - protected function createDummyUserKeys($uid) { - $this->loginAsUser($uid); - - $this->view->mkdir($uid . '/files_encryption/'); - $this->view->mkdir('/files_encryption/public_keys'); - $this->view->file_put_contents($uid . '/files_encryption/' . $uid . '.privateKey', 'privateKey'); - $this->view->file_put_contents('/files_encryption/public_keys/' . $uid . '.publicKey', 'publicKey'); - } - - /** - * @param string $uid - */ - protected function createDummyFileKeys($uid) { - $this->loginAsUser($uid); - - $this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3'); - $this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2'); - $this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1'); - $this->view->mkdir($uid . '/files_encryption/keys/folder2/file.2.1'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/fileKey' , 'data'); - } - - /** - * @param string $uid - */ - protected function createDummyFiles($uid) { - $this->loginAsUser($uid); - - $this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3'); - $this->view->mkdir($uid . '/files/folder1/folder2/file2'); - $this->view->mkdir($uid . '/files/folder1/file.1'); - $this->view->mkdir($uid . '/files/folder2/file.2.1'); - $this->view->file_put_contents($uid . '/files/folder1/folder2/folder3/file3/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files/folder1/folder2/file2/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files/folder1/file.1/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files/folder2/file.2.1/fileKey' , 'data'); - } - - /** - * @param string $uid - */ - protected function createDummyFilesInTrash($uid) { - $this->loginAsUser($uid); - - $this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864'); - $this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2'); - $this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - $this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); - - $this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/fileKey' , 'data'); - $this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/fileKey' , 'data'); - - // create the files itself - $this->view->mkdir($uid . '/files_trashbin/folder1.d7437648723'); - $this->view->file_put_contents($uid . '/files_trashbin/file1.d5457864' , 'data'); - $this->view->file_put_contents($uid . '/files_trashbin/folder1.d7437648723/file2' , 'data'); - } - - protected function createDummySystemWideKeys() { - $this->view->mkdir('files_encryption'); - $this->view->mkdir('files_encryption/public_keys'); - $this->view->file_put_contents('files_encryption/systemwide_1.privateKey', 'data'); - $this->view->file_put_contents('files_encryption/systemwide_2.privateKey', 'data'); - $this->view->file_put_contents('files_encryption/public_keys/systemwide_1.publicKey', 'data'); - $this->view->file_put_contents('files_encryption/public_keys/systemwide_2.publicKey', 'data'); - } - - public function testMigrateToNewFolderStructure() { - $this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); - $this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); - $this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); - - $this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); - $this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); - $this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); - - $this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); - $this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); - $this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); - - $this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER1); - $this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER2); - $this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER3); - - $this->createDummyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2); - - // no user for system wide mount points - $this->createDummyFileKeys(''); - $this->createDummyShareKeys(''); - - $this->createDummySystemWideKeys(); - - /** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Encryption\Migration $m */ - $m = $this->getMockBuilder('OCA\Encryption\Migration') - ->setConstructorArgs( - [ - \OC::$server->getConfig(), - new \OC\Files\View(), - \OC::$server->getDatabaseConnection(), - $this->logger - ] - )->setMethods(['getSystemMountPoints'])->getMock(); - - $m->expects($this->any())->method('getSystemMountPoints') - ->will($this->returnValue([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']])); - - $m->reorganizeFolderStructure(); - // even if it runs twice folder should always move only once - $m->reorganizeFolderStructure(); - - $this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1); - - $this->assertTrue( - $this->view->file_exists( - self::TEST_ENCRYPTION_MIGRATION_USER1 . '/files_encryption/' . - $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.publicKey') - ); - - $this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER2); - - $this->assertTrue( - $this->view->file_exists( - self::TEST_ENCRYPTION_MIGRATION_USER2 . '/files_encryption/' . - $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.publicKey') - ); - - $this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER3); - - $this->assertTrue( - $this->view->file_exists( - self::TEST_ENCRYPTION_MIGRATION_USER3 . '/files_encryption/' . - $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.publicKey') - ); - - $this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1); - - $this->assertTrue( - $this->view->file_exists( - '/files_encryption/' . $this->moduleId . '/systemwide_1.publicKey') - ); - $this->assertTrue( - $this->view->file_exists( - '/files_encryption/' . $this->moduleId . '/systemwide_2.publicKey') - ); - - $this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER1); - $this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER2); - $this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER3); - // system wide keys - $this->verifyNewKeyPath(''); - // trash - $this->verifyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2); - - } - - /** - * @param string $uid - */ - protected function verifyFilesInTrash($uid) { - $this->loginAsUser($uid); - - // share keys - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') - ); - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') - ); - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/folder1.d7437648723/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') - ); - - // file keys - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/fileKey') - ); - - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/fileKey') - ); - $this->assertTrue( - $this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/folder1.d7437648723/file2/' . $this->moduleId . '/fileKey') - ); - } - - /** - * @param string $uid - */ - protected function verifyNewKeyPath($uid) { - // private key - if ($uid !== '') { - $this->loginAsUser($uid); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/' . $this->moduleId . '/'. $uid . '.privateKey')); - } - // file keys - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/fileKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/fileKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/fileKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' .$this->moduleId . '/fileKey')); - // share keys - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); - if ($this->public_share_key_id) { - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . $this->public_share_key_id . '.shareKey')); - } - if ($this->recovery_key_id) { - $this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . $this->recovery_key_id . '.shareKey')); - } - } - - private function prepareDB() { - $config = \OC::$server->getConfig(); - $config->setAppValue('files_encryption', 'recoveryKeyId', 'recovery_id'); - $config->setAppValue('files_encryption', 'publicShareKeyId', 'share_id'); - $config->setAppValue('files_encryption', 'recoveryAdminEnabled', '1'); - $config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'files_encryption', 'recoverKeyEnabled', '1'); - - //$this->invokePrivate($config, 'cache', [[]]); - $cache = $this->invokePrivate(\OC::$server->getAppConfig(), 'cache'); - unset($cache['encryption']); - unset($cache['files_encryption']); - $this->invokePrivate(\OC::$server->getAppConfig(), 'cache', [$cache]); - - // delete default values set by the encryption app during initialization - - /** @var \OCP\IDBConnection $connection */ - $connection = \OC::$server->getDatabaseConnection(); - $query = $connection->getQueryBuilder(); - $query->delete('appconfig') - ->where($query->expr()->eq('appid', $query->createParameter('appid'))) - ->setParameter('appid', 'encryption'); - $query->execute(); - $query = $connection->getQueryBuilder(); - $query->delete('preferences') - ->where($query->expr()->eq('appid', $query->createParameter('appid'))) - ->setParameter('appid', 'encryption'); - $query->execute(); - } - - public function testUpdateDB() { - $this->prepareDB(); - - $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); - $this->invokePrivate($m, 'installedVersion', ['0.7']); - $m->updateDB(); - - $this->verifyDB('appconfig', 'files_encryption', 0); - $this->verifyDB('preferences', 'files_encryption', 0); - $this->verifyDB('appconfig', 'encryption', 3); - $this->verifyDB('preferences', 'encryption', 1); - - } - - /** - * test update db if the db already contain some existing new values - */ - public function testUpdateDBExistingNewConfig() { - $this->prepareDB(); - $config = \OC::$server->getConfig(); - $config->setAppValue('encryption', 'publicShareKeyId', 'wrong_share_id'); - $config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9'); - - $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); - $this->invokePrivate($m, 'installedVersion', ['0.7']); - $m->updateDB(); - - $this->verifyDB('appconfig', 'files_encryption', 0); - $this->verifyDB('preferences', 'files_encryption', 0); - $this->verifyDB('appconfig', 'encryption', 3); - $this->verifyDB('preferences', 'encryption', 1); - - // check if the existing values where overwritten correctly - /** @var \OC\DB\Connection $connection */ - $connection = \OC::$server->getDatabaseConnection(); - $query = $connection->getQueryBuilder(); - $query->select('configvalue') - ->from('appconfig') - ->where($query->expr()->andX( - $query->expr()->eq('appid', $query->createParameter('appid')), - $query->expr()->eq('configkey', $query->createParameter('configkey')) - )) - ->setParameter('appid', 'encryption') - ->setParameter('configkey', 'publicShareKeyId'); - $result = $query->execute(); - $value = $result->fetch(); - $this->assertTrue(isset($value['configvalue'])); - $this->assertSame('share_id', $value['configvalue']); - - $query = $connection->getQueryBuilder(); - $query->select('configvalue') - ->from('preferences') - ->where($query->expr()->andX( - $query->expr()->eq('appid', $query->createParameter('appid')), - $query->expr()->eq('configkey', $query->createParameter('configkey')), - $query->expr()->eq('userid', $query->createParameter('userid')) - )) - ->setParameter('appid', 'encryption') - ->setParameter('configkey', 'recoverKeyEnabled') - ->setParameter('userid', self::TEST_ENCRYPTION_MIGRATION_USER1); - $result = $query->execute(); - $value = $result->fetch(); - $this->assertTrue(isset($value['configvalue'])); - $this->assertSame('1', $value['configvalue']); - - } - - /** - * @param string $table - * @param string $appid - * @param integer $expected - */ - public function verifyDB($table, $appid, $expected) { - /** @var \OCP\IDBConnection $connection */ - $connection = \OC::$server->getDatabaseConnection(); - $query = $connection->getQueryBuilder(); - $query->select('appid') - ->from($table) - ->where($query->expr()->eq('appid', $query->createParameter('appid'))) - ->setParameter('appid', $appid); - $result = $query->execute(); - $values = $result->fetchAll(); - $this->assertSame($expected, - count($values) - ); - } - - /** - * test update of the file cache - */ - public function testUpdateFileCache() { - $this->prepareFileCache(); - $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); - $this->invokePrivate($m, 'installedVersion', ['0.7']); - self::invokePrivate($m, 'updateFileCache'); - - // check results - - /** @var \OCP\IDBConnection $connection */ - $connection = \OC::$server->getDatabaseConnection(); - $query = $connection->getQueryBuilder(); - $query->select('*') - ->from('filecache'); - $result = $query->execute(); - $entries = $result->fetchAll(); - foreach($entries as $entry) { - if ((int)$entry['encrypted'] === 1) { - $this->assertSame((int)$entry['unencrypted_size'], (int)$entry['size']); - } else { - $this->assertSame((int)$entry['unencrypted_size'] - 2, (int)$entry['size']); - } - } - - - } - - public function prepareFileCache() { - /** @var \OCP\IDBConnection $connection */ - $connection = \OC::$server->getDatabaseConnection(); - $query = $connection->getQueryBuilder(); - $query->delete('filecache'); - $query->execute(); - $query = $connection->getQueryBuilder(); - $result = $query->select('fileid') - ->from('filecache') - ->setMaxResults(1)->execute()->fetchAll(); - $this->assertEmpty($result); - $query = $connection->getQueryBuilder(); - $query->insert('filecache') - ->values( - array( - 'storage' => $query->createParameter('storage'), - 'path_hash' => $query->createParameter('path_hash'), - 'encrypted' => $query->createParameter('encrypted'), - 'size' => $query->createParameter('size'), - 'unencrypted_size' => $query->createParameter('unencrypted_size'), - ) - ); - for ($i = 1; $i < 20; $i++) { - $query->setParameter('storage', 1) - ->setParameter('path_hash', $i) - ->setParameter('encrypted', $i % 2) - ->setParameter('size', $i) - ->setParameter('unencrypted_size', $i + 2); - $this->assertSame(1, - $query->execute() - ); - } - $query = $connection->getQueryBuilder(); - $result = $query->select('fileid') - ->from('filecache') - ->execute()->fetchAll(); - $this->assertSame(19, count($result)); - } - - /** - * @dataProvider dataTestGetTargetDir - */ - public function testGetTargetDir($user, $keyPath, $filename, $trash, $systemMounts, $expected) { - - $updater = $this->getMockBuilder('\OC\Files\Cache\Updater') - ->disableOriginalConstructor()->getMock(); - $view = $this->getMockBuilder('\OC\Files\View') - ->disableOriginalConstructor()->getMock(); - $view->expects($this->any())->method('file_exists')->willReturn(true); - $view->expects($this->any())->method('getUpdater')->willReturn($updater); - - - $m = $this->getMockBuilder('OCA\Encryption\Migration') - ->setConstructorArgs( - [ - \OC::$server->getConfig(), - $view, - \OC::$server->getDatabaseConnection(), - $this->logger - ] - )->setMethods(['getSystemMountPoints'])->getMock(); - - $m->expects($this->any())->method('getSystemMountPoints') - ->willReturn($systemMounts); - - $this->assertSame($expected, - $this->invokePrivate($m, 'getTargetDir', [$user, $keyPath, $filename, $trash]) - ); - } - - public function dataTestGetTargetDir() { - return [ - [ - 'user1', - '/files_encryption/keys/foo/bar.txt', - 'user1.shareKey', - false, - [], - 'user1/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' - ], - [ - 'user1', - '/files_trashbin/keys/foo/bar.txt', - 'user1.shareKey', - true, - [], - 'user1/files_encryption/keys/files_trashbin/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' - ], - [ - '', - '/files_encryption/keys/foo/bar.txt', - 'user1.shareKey', - false, - [['mountpoint' => 'foo']], - '/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' - ], - [ - '', - '/files_encryption/keys/foo/bar.txt', - 'user1.shareKey', - false, - [['mountpoint' => 'foobar']], - false - ], - [ - '', - '/files_encryption/keys/foobar/bar.txt', - 'user1.shareKey', - false, - [['mountpoint' => 'foo']], - false - ] - ]; - } - -} diff --git a/apps/encryption/tests/lib/UtilTest.php b/apps/encryption/tests/lib/UtilTest.php deleted file mode 100644 index fa90125d6e9..00000000000 --- a/apps/encryption/tests/lib/UtilTest.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests; - - -use OCA\Encryption\Util; -use Test\TestCase; - -class UtilTest extends TestCase { - private static $tempStorage = []; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $configMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $filesMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $userManagerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $mountMock; - - /** @var Util */ - private $instance; - - public function testSetRecoveryForUser() { - $this->instance->setRecoveryForUser('1'); - $this->assertArrayHasKey('recoveryEnabled', self::$tempStorage); - } - - public function testIsRecoveryEnabledForUser() { - $this->assertTrue($this->instance->isRecoveryEnabledForUser('admin')); - - // Assert recovery will return default value if not set - unset(self::$tempStorage['recoveryEnabled']); - $this->assertEquals(0, $this->instance->isRecoveryEnabledForUser('admin')); - } - - public function testUserHasFiles() { - $this->filesMock->expects($this->once()) - ->method('file_exists') - ->will($this->returnValue(true)); - - $this->assertTrue($this->instance->userHasFiles('admin')); - } - - protected function setUp() { - parent::setUp(); - $this->mountMock = $this->getMock('\OCP\Files\Mount\IMountPoint'); - $this->filesMock = $this->getMock('OC\Files\View'); - $this->userManagerMock = $this->getMock('\OCP\IUserManager'); - - $cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') - ->disableOriginalConstructor() - ->getMock(); - $loggerMock = $this->getMock('OCP\ILogger'); - $userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'getUID', - 'login', - 'logout', - 'setUser', - 'getUser' - ]) - ->getMock(); - - $userSessionMock->method('isLoggedIn')->will($this->returnValue(true)); - - $userSessionMock->method('getUID')->will($this->returnValue('admin')); - - $userSessionMock->expects($this->any()) - ->method($this->anything()) - ->will($this->returnSelf()); - - - $this->configMock = $configMock = $this->getMock('OCP\IConfig'); - - $this->configMock->expects($this->any()) - ->method('getUserValue') - ->will($this->returnCallback([$this, 'getValueTester'])); - - $this->configMock->expects($this->any()) - ->method('setUserValue') - ->will($this->returnCallback([$this, 'setValueTester'])); - - $this->instance = new Util($this->filesMock, $cryptMock, $loggerMock, $userSessionMock, $configMock, $this->userManagerMock); - } - - /** - * @param $userId - * @param $app - * @param $key - * @param $value - */ - public function setValueTester($userId, $app, $key, $value) { - self::$tempStorage[$key] = $value; - } - - /** - * @param $userId - * @param $app - * @param $key - * @param $default - * @return mixed - */ - public function getValueTester($userId, $app, $key, $default) { - if (!empty(self::$tempStorage[$key])) { - return self::$tempStorage[$key]; - } - return $default ?: null; - } - - /** - * @dataProvider dataTestIsMasterKeyEnabled - * - * @param string $value - * @param bool $expect - */ - public function testIsMasterKeyEnabled($value, $expect) { - $this->configMock->expects($this->once())->method('getAppValue') - ->with('encryption', 'useMasterKey', '0')->willReturn($value); - $this->assertSame($expect, - $this->instance->isMasterKeyEnabled() - ); - } - - public function dataTestIsMasterKeyEnabled() { - return [ - ['0', false], - ['1', true] - ]; - } - - /** - * @dataProvider dataTestShouldEncryptHomeStorage - * @param $returnValue return value from getAppValue() - * @param $expected - */ - public function testShouldEncryptHomeStorage($returnValue, $expected) { - $this->configMock->expects($this->once())->method('getAppValue') - ->with('encryption', 'encryptHomeStorage', '1') - ->willReturn($returnValue); - - $this->assertSame($expected, - $this->instance->shouldEncryptHomeStorage()); - } - - public function dataTestShouldEncryptHomeStorage() { - return [ - ['1', true], - ['0', false] - ]; - } - - /** - * @dataProvider dataTestSetEncryptHomeStorage - * @param $value - * @param $expected - */ - public function testSetEncryptHomeStorage($value, $expected) { - $this->configMock->expects($this->once())->method('setAppValue') - ->with('encryption', 'encryptHomeStorage', $expected); - $this->instance->setEncryptHomeStorage($value); - } - - public function dataTestSetEncryptHomeStorage() { - return [ - [true, '1'], - [false, '0'] - ]; - } - - public function testGetStorage() { - $path = '/foo/bar.txt'; - $this->filesMock->expects($this->once())->method('getMount')->with($path) - ->willReturn($this->mountMock); - $this->mountMock->expects($this->once())->method('getStorage')->willReturn(true); - - $this->assertTrue($this->instance->getStorage($path)); - } - -} diff --git a/apps/encryption/tests/lib/crypto/encryptalltest.php b/apps/encryption/tests/lib/crypto/encryptalltest.php deleted file mode 100644 index 04d931342a7..00000000000 --- a/apps/encryption/tests/lib/crypto/encryptalltest.php +++ /dev/null @@ -1,291 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests\lib\Crypto; - - -use OCA\Encryption\Crypto\EncryptAll; -use Test\TestCase; - -class EncryptAllTest extends TestCase { - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCA\Encryption\KeyManager */ - protected $keyManager; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IUserManager */ - protected $userManager; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCA\Encryption\Users\Setup */ - protected $setupUser; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\View */ - protected $view; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IConfig */ - protected $config; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\Mail\IMailer */ - protected $mailer; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IL10N */ - protected $l; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Helper\QuestionHelper */ - protected $questionHelper; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Input\InputInterface */ - protected $inputInterface; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Output\OutputInterface */ - protected $outputInterface; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\UserInterface */ - protected $userInterface; - - /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\Security\ISecureRandom */ - protected $secureRandom; - - /** @var EncryptAll */ - protected $encryptAll; - - function setUp() { - parent::setUp(); - $this->setupUser = $this->getMockBuilder('OCA\Encryption\Users\Setup') - ->disableOriginalConstructor()->getMock(); - $this->keyManager = $this->getMockBuilder('OCA\Encryption\KeyManager') - ->disableOriginalConstructor()->getMock(); - $this->userManager = $this->getMockBuilder('OCP\IUserManager') - ->disableOriginalConstructor()->getMock(); - $this->view = $this->getMockBuilder('OC\Files\View') - ->disableOriginalConstructor()->getMock(); - $this->config = $this->getMockBuilder('OCP\IConfig') - ->disableOriginalConstructor()->getMock(); - $this->mailer = $this->getMockBuilder('OCP\Mail\IMailer') - ->disableOriginalConstructor()->getMock(); - $this->l = $this->getMockBuilder('OCP\IL10N') - ->disableOriginalConstructor()->getMock(); - $this->questionHelper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor()->getMock(); - $this->inputInterface = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface') - ->disableOriginalConstructor()->getMock(); - $this->outputInterface = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface') - ->disableOriginalConstructor()->getMock(); - $this->userInterface = $this->getMockBuilder('OCP\UserInterface') - ->disableOriginalConstructor()->getMock(); - - - $this->outputInterface->expects($this->any())->method('getFormatter') - ->willReturn($this->getMock('\Symfony\Component\Console\Formatter\OutputFormatterInterface')); - - $this->userManager->expects($this->any())->method('getBackends')->willReturn([$this->userInterface]); - $this->userInterface->expects($this->any())->method('getUsers')->willReturn(['user1', 'user2']); - - $this->secureRandom = $this->getMockBuilder('OCP\Security\ISecureRandom')->disableOriginalConstructor()->getMock(); - $this->secureRandom->expects($this->any())->method('getMediumStrengthGenerator')->willReturn($this->secureRandom); - $this->secureRandom->expects($this->any())->method('getLowStrengthGenerator')->willReturn($this->secureRandom); - $this->secureRandom->expects($this->any())->method('generate')->willReturn('12345678'); - - - $this->encryptAll = new EncryptAll( - $this->setupUser, - $this->userManager, - $this->view, - $this->keyManager, - $this->config, - $this->mailer, - $this->l, - $this->questionHelper, - $this->secureRandom - ); - } - - public function testEncryptAll() { - /** @var EncryptAll | \PHPUnit_Framework_MockObject_MockObject $encryptAll */ - $encryptAll = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll') - ->setConstructorArgs( - [ - $this->setupUser, - $this->userManager, - $this->view, - $this->keyManager, - $this->config, - $this->mailer, - $this->l, - $this->questionHelper, - $this->secureRandom - ] - ) - ->setMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords']) - ->getMock(); - - $encryptAll->expects($this->at(0))->method('createKeyPairs')->with(); - $encryptAll->expects($this->at(1))->method('encryptAllUsersFiles')->with(); - $encryptAll->expects($this->at(2))->method('outputPasswords')->with(); - - $encryptAll->encryptAll($this->inputInterface, $this->outputInterface); - - } - - public function testCreateKeyPairs() { - /** @var EncryptAll | \PHPUnit_Framework_MockObject_MockObject $encryptAll */ - $encryptAll = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll') - ->setConstructorArgs( - [ - $this->setupUser, - $this->userManager, - $this->view, - $this->keyManager, - $this->config, - $this->mailer, - $this->l, - $this->questionHelper, - $this->secureRandom - ] - ) - ->setMethods(['setupUserFS', 'generateOneTimePassword']) - ->getMock(); - - - // set protected property $output - $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]); - - $this->keyManager->expects($this->exactly(2))->method('userHasKeys') - ->willReturnCallback( - function ($user) { - if ($user === 'user1') { - return false; - } - return true; - } - ); - - $encryptAll->expects($this->once())->method('setupUserFS')->with('user1'); - $encryptAll->expects($this->once())->method('generateOneTimePassword')->with('user1')->willReturn('password'); - $this->setupUser->expects($this->once())->method('setupUser')->with('user1', 'password'); - - $this->invokePrivate($encryptAll, 'createKeyPairs'); - - $userPasswords = $this->invokePrivate($encryptAll, 'userPasswords'); - - // we only expect the skipped user, because generateOneTimePassword which - // would set the user with the new password was mocked. - // This method will be tested separately - $this->assertSame(1, count($userPasswords)); - $this->assertSame('', $userPasswords['user2']); - } - - public function testEncryptAllUsersFiles() { - /** @var EncryptAll | \PHPUnit_Framework_MockObject_MockObject $encryptAll */ - $encryptAll = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll') - ->setConstructorArgs( - [ - $this->setupUser, - $this->userManager, - $this->view, - $this->keyManager, - $this->config, - $this->mailer, - $this->l, - $this->questionHelper, - $this->secureRandom - ] - ) - ->setMethods(['encryptUsersFiles']) - ->getMock(); - - // set protected property $output - $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]); - $this->invokePrivate($encryptAll, 'userPasswords', [['user1' => 'pwd1', 'user2' => 'pwd2']]); - - $encryptAll->expects($this->at(0))->method('encryptUsersFiles')->with('user1'); - $encryptAll->expects($this->at(1))->method('encryptUsersFiles')->with('user2'); - - $this->invokePrivate($encryptAll, 'encryptAllUsersFiles'); - - } - - public function testEncryptUsersFiles() { - /** @var EncryptAll | \PHPUnit_Framework_MockObject_MockObject $encryptAll */ - $encryptAll = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll') - ->setConstructorArgs( - [ - $this->setupUser, - $this->userManager, - $this->view, - $this->keyManager, - $this->config, - $this->mailer, - $this->l, - $this->questionHelper, - $this->secureRandom - ] - ) - ->setMethods(['encryptFile']) - ->getMock(); - - - $this->view->expects($this->at(0))->method('getDirectoryContent') - ->with('/user1/files')->willReturn( - [ - ['name' => 'foo', 'type'=>'dir'], - ['name' => 'bar', 'type'=>'file'], - ] - ); - - $this->view->expects($this->at(3))->method('getDirectoryContent') - ->with('/user1/files/foo')->willReturn( - [ - ['name' => 'subfile', 'type'=>'file'] - ] - ); - - $this->view->expects($this->any())->method('is_dir') - ->willReturnCallback( - function($path) { - if ($path === '/user1/files/foo') { - return true; - } - return false; - } - ); - - $encryptAll->expects($this->at(0))->method('encryptFile')->with('/user1/files/bar'); - $encryptAll->expects($this->at(1))->method('encryptFile')->with('/user1/files/foo/subfile'); - - $progressBar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar') - ->disableOriginalConstructor()->getMock(); - - $this->invokePrivate($encryptAll, 'encryptUsersFiles', ['user1', $progressBar, '']); - - } - - public function testGenerateOneTimePassword() { - $password = $this->invokePrivate($this->encryptAll, 'generateOneTimePassword', ['user1']); - $this->assertTrue(is_string($password)); - $this->assertSame(8, strlen($password)); - - $userPasswords = $this->invokePrivate($this->encryptAll, 'userPasswords'); - $this->assertSame(1, count($userPasswords)); - $this->assertSame($password, $userPasswords['user1']); - } - -} diff --git a/apps/encryption/tests/lib/users/SetupTest.php b/apps/encryption/tests/lib/users/SetupTest.php deleted file mode 100644 index 0cc59384b16..00000000000 --- a/apps/encryption/tests/lib/users/SetupTest.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Encryption\Tests\Users; - - -use OCA\Encryption\Users\Setup; -use Test\TestCase; - -class SetupTest extends TestCase { - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $keyManagerMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $cryptMock; - /** - * @var Setup - */ - private $instance; - - public function testSetupServerSide() { - $this->keyManagerMock->expects($this->exactly(2))->method('validateShareKey'); - $this->keyManagerMock->expects($this->exactly(2))->method('validateMasterKey'); - $this->keyManagerMock->expects($this->exactly(2)) - ->method('userHasKeys') - ->with('admin') - ->willReturnOnConsecutiveCalls(true, false); - - $this->assertTrue($this->instance->setupServerSide('admin', - 'password')); - - $this->keyManagerMock->expects($this->once()) - ->method('storeKeyPair') - ->with('admin', 'password') - ->willReturn(false); - - $this->assertFalse($this->instance->setupServerSide('admin', - 'password')); - } - - protected function setUp() { - parent::setUp(); - $logMock = $this->getMock('OCP\ILogger'); - $userSessionMock = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->getMock(); - $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') - ->disableOriginalConstructor() - ->getMock(); - - $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->instance = new Setup($logMock, - $userSessionMock, - $this->cryptMock, - $this->keyManagerMock); - } - -} |