diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php new file mode 100644 index 0000000000..480baab6ba --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php @@ -0,0 +1,50 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @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 + * + */ + +namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest; + +use OC\Files\View; +use Test\Traits\EncryptionTrait; + +/** + * Class EncryptionMasterKeyUploadTest + * + * @group DB + * + * @package OCA\DAV\Tests\Unit\Connector\Sabre\RequestTest + */ +class EncryptionMasterKeyUploadTest extends UploadTest { + use EncryptionTrait; + + protected function setupUser($name, $password) { + $this->createUser($name, $password); + $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); + $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); + // we use the master key + \OC::$server->getConfig()->setAppValue('encryption', 'useMasterKey', '1'); + $this->setupForUser($name, $password); + $this->loginWithEncryption($name); + return new View('/' . $name . '/files'); + } +} diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php index e65d58b816..c0cba12138 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php @@ -41,6 +41,8 @@ class EncryptionUploadTest extends UploadTest { $this->createUser($name, $password); $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); + // we use per-user keys + \OC::$server->getConfig()->setAppValue('encryption', 'useMasterKey', '0'); $this->setupForUser($name, $password); $this->loginWithEncryption($name); return new View('/' . $name . '/files'); diff --git a/apps/encryption/appinfo/app.php b/apps/encryption/appinfo/app.php index 950166dca2..4f54f0e725 100644 --- a/apps/encryption/appinfo/app.php +++ b/apps/encryption/appinfo/app.php @@ -31,4 +31,5 @@ $app = new Application([], $encryptionSystemReady); if ($encryptionSystemReady) { $app->registerEncryptionModule(); $app->registerHooks(); + $app->setUp(); } diff --git a/apps/encryption/appinfo/info.xml b/apps/encryption/appinfo/info.xml index 7cfdc93438..f35a87aa4f 100644 --- a/apps/encryption/appinfo/info.xml +++ b/apps/encryption/appinfo/info.xml @@ -19,7 +19,7 @@ user-encryption admin-encryption - 1.7.1 + 2.0.0 @@ -33,6 +33,13 @@ OCA\Encryption\Command\EnableMasterKey + OCA\Encryption\Command\DisableMasterKey OCA\Encryption\Command\MigrateKeys + + + + OCA\Encryption\Migration\SetMasterKeyStatus + + diff --git a/apps/encryption/lib/AppInfo/Application.php b/apps/encryption/lib/AppInfo/Application.php index 56c2dafdab..dd9d173c8e 100644 --- a/apps/encryption/lib/AppInfo/Application.php +++ b/apps/encryption/lib/AppInfo/Application.php @@ -67,7 +67,11 @@ class Application extends \OCP\AppFramework\App { $session = $this->getContainer()->query('Session'); $session->setStatus(Session::RUN_MIGRATION); } - if ($this->encryptionManager->isEnabled() && $encryptionSystemReady) { + + } + + public function setUp() { + if ($this->encryptionManager->isEnabled()) { /** @var Setup $setup */ $setup = $this->getContainer()->query('UserSetup'); $setup->setupSystem(); @@ -77,7 +81,6 @@ class Application extends \OCP\AppFramework\App { /** * register hooks */ - public function registerHooks() { if (!$this->config->getSystemValue('maintenance', false)) { @@ -193,7 +196,8 @@ class Application extends \OCP\AppFramework\App { $c->getAppName(), $server->getRequest(), $server->getL10N($c->getAppName()), - $c->query('Session') + $c->query('Session'), + $server->getEncryptionManager() ); }); diff --git a/apps/encryption/lib/Command/DisableMasterKey.php b/apps/encryption/lib/Command/DisableMasterKey.php new file mode 100644 index 0000000000..97c2ad40b6 --- /dev/null +++ b/apps/encryption/lib/Command/DisableMasterKey.php @@ -0,0 +1,89 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Encryption\Command; + + +use OCA\Encryption\Util; +use OCP\IConfig; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +class DisableMasterKey extends Command { + + /** @var Util */ + protected $util; + + /** @var IConfig */ + protected $config; + + /** @var QuestionHelper */ + protected $questionHelper; + + /** + * @param Util $util + * @param IConfig $config + * @param QuestionHelper $questionHelper + */ + public function __construct(Util $util, + IConfig $config, + QuestionHelper $questionHelper) { + + $this->util = $util; + $this->config = $config; + $this->questionHelper = $questionHelper; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('encryption:disable-master-key') + ->setDescription('Disable the master key and use per-user keys instead. Only available for fresh installations with no existing encrypted data! There is no way to enable it again.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + + $isMasterKeyEnabled = $this->util->isMasterKeyEnabled(); + + if(!$isMasterKeyEnabled) { + $output->writeln('Master key already disabled'); + } else { + $question = new ConfirmationQuestion( + 'Warning: Only perform this operation for a fresh installations with no existing encrypted data! ' + . 'There is no way to enable the master key again. ' + . 'We strongly recommend to keep the master key, it provides significant performance improvements ' + . 'and is easier to handle for both, users and administrators. ' + . 'Do you really want to switch to per-user keys? (y/n) ', false); + if ($this->questionHelper->ask($input, $output, $question)) { + $this->config->setAppValue('encryption', 'useMasterKey', '0'); + $output->writeln('Master key successfully disabled.'); + } else { + $output->writeln('aborted.'); + } + } + + } + +} diff --git a/apps/encryption/lib/Controller/StatusController.php b/apps/encryption/lib/Controller/StatusController.php index 0776a84ceb..9ec9fd1234 100644 --- a/apps/encryption/lib/Controller/StatusController.php +++ b/apps/encryption/lib/Controller/StatusController.php @@ -28,6 +28,7 @@ namespace OCA\Encryption\Controller; use OCA\Encryption\Session; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; +use OCP\Encryption\IManager; use OCP\IL10N; use OCP\IRequest; @@ -39,20 +40,26 @@ class StatusController extends Controller { /** @var Session */ private $session; + /** @var IManager */ + private $encryptionManager; + /** * @param string $AppName * @param IRequest $request * @param IL10N $l10n * @param Session $session + * @param IManager $encryptionManager */ public function __construct($AppName, IRequest $request, IL10N $l10n, - Session $session + Session $session, + IManager $encryptionManager ) { parent::__construct($AppName, $request); $this->l = $l10n; $this->session = $session; + $this->encryptionManager = $encryptionManager; } /** @@ -78,9 +85,15 @@ class StatusController extends Controller { break; case Session::NOT_INITIALIZED: $status = 'interactionNeeded'; - $message = (string)$this->l->t( - 'Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again.' - ); + if ($this->encryptionManager->isEnabled()) { + $message = (string)$this->l->t( + 'Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again.' + ); + } else { + $message = (string)$this->l->t( + 'Please enable server side encryption in the admin settings in order to use the encryption module.' + ); + } break; case Session::INIT_SUCCESSFUL: $status = 'success'; diff --git a/apps/encryption/lib/Crypto/Encryption.php b/apps/encryption/lib/Crypto/Encryption.php index 7f7665a24f..6869177ac3 100644 --- a/apps/encryption/lib/Crypto/Encryption.php +++ b/apps/encryption/lib/Crypto/Encryption.php @@ -569,4 +569,13 @@ class Encryption implements IEncryptionModule { public function isReadyForUser($user) { return $this->keyManager->userHasKeys($user); } + + /** + * We only need a detailed access list if the master key is not enabled + * + * @return bool + */ + public function needDetailedAccessList() { + return !$this->util->isMasterKeyEnabled(); + } } diff --git a/apps/encryption/lib/KeyManager.php b/apps/encryption/lib/KeyManager.php index 6b260c39bf..6039aaaaa0 100644 --- a/apps/encryption/lib/KeyManager.php +++ b/apps/encryption/lib/KeyManager.php @@ -179,8 +179,8 @@ class KeyManager { return; } - $masterKey = $this->getPublicMasterKey(); - if (empty($masterKey)) { + $publicMasterKey = $this->getPublicMasterKey(); + if (empty($publicMasterKey)) { $keyPair = $this->crypt->createKeyPair(); // Save public key @@ -193,6 +193,15 @@ class KeyManager { $header = $this->crypt->generateHeader(); $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey); } + + if (!$this->session->isPrivateKeySet()) { + $masterKey = $this->getSystemPrivateKey($this->masterKeyId); + $decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId); + $this->session->setPrivateKey($decryptedMasterKey); + } + + // after the encryption key is available we are ready to go + $this->session->setStatus(Session::INIT_SUCCESSFUL); } /** diff --git a/apps/encryption/lib/Migration/SetMasterKeyStatus.php b/apps/encryption/lib/Migration/SetMasterKeyStatus.php new file mode 100644 index 0000000000..a21d0acae2 --- /dev/null +++ b/apps/encryption/lib/Migration/SetMasterKeyStatus.php @@ -0,0 +1,77 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Encryption\Migration; + + +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +/** + * Class SetPasswordColumn + * + * @package OCA\Files_Sharing\Migration + */ +class SetMasterKeyStatus implements IRepairStep { + + + /** @var IConfig */ + private $config; + + + public function __construct(IConfig $config) { + $this->config = $config; + } + + /** + * Returns the step's name + * + * @return string + * @since 9.1.0 + */ + public function getName() { + return 'Write default encryption module configuration to the database'; + } + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + if (!$this->shouldRun()) { + return; + } + + // if no config for the master key is set we set it explicitly to '0' in + // order not to break old installations because the default changed to '1'. + $configAlreadySet = $this->config->getAppValue('encryption', 'useMasterKey', false); + if ($configAlreadySet === false) { + $this->config->setAppValue('encryption', 'useMasterKey', '0'); + } + } + + protected function shouldRun() { + $appVersion = $this->config->getAppValue('encryption', 'installed_version', '0.0.0'); + return version_compare($appVersion, '2.0.0', '<'); + } + +} diff --git a/apps/encryption/lib/Util.php b/apps/encryption/lib/Util.php index 72afa68aad..d6ae9bd7e5 100644 --- a/apps/encryption/lib/Util.php +++ b/apps/encryption/lib/Util.php @@ -136,7 +136,7 @@ class Util { * @return bool */ public function isMasterKeyEnabled() { - $userMasterKey = $this->config->getAppValue('encryption', 'useMasterKey', '0'); + $userMasterKey = $this->config->getAppValue('encryption', 'useMasterKey', '1'); return ($userMasterKey === '1'); } diff --git a/apps/encryption/templates/settings-admin.php b/apps/encryption/templates/settings-admin.php index efe9c44ece..c5f8d9f553 100644 --- a/apps/encryption/templates/settings-admin.php +++ b/apps/encryption/templates/settings-admin.php @@ -7,7 +7,7 @@ style('encryption', 'settings-admin'); ?>

t("Default encryption module")); ?>

- + t("Encryption app is enabled but your keys are not initialized, please log-out and log-in again")); ?>

diff --git a/apps/encryption/tests/Controller/StatusControllerTest.php b/apps/encryption/tests/Controller/StatusControllerTest.php index c6c92e2aac..ee0f7b2661 100644 --- a/apps/encryption/tests/Controller/StatusControllerTest.php +++ b/apps/encryption/tests/Controller/StatusControllerTest.php @@ -27,6 +27,7 @@ namespace OCA\Encryption\Tests\Controller; use OCA\Encryption\Controller\StatusController; use OCA\Encryption\Session; +use OCP\Encryption\IManager; use OCP\IRequest; use Test\TestCase; @@ -41,6 +42,9 @@ class StatusControllerTest extends TestCase { /** @var \OCA\Encryption\Session | \PHPUnit_Framework_MockObject_MockObject */ protected $sessionMock; + /** @var IManager | \PHPUnit_Framework_MockObject_MockObject */ + protected $encryptionManagerMock; + /** @var StatusController */ protected $controller; @@ -59,11 +63,13 @@ class StatusControllerTest extends TestCase { ->will($this->returnCallback(function($message) { return $message; })); + $this->encryptionManagerMock = $this->createMock(IManager::class); $this->controller = new StatusController('encryptionTest', $this->requestMock, $this->l10nMock, - $this->sessionMock); + $this->sessionMock, + $this->encryptionManagerMock); } diff --git a/apps/encryption/tests/UtilTest.php b/apps/encryption/tests/UtilTest.php index d2f1d40e16..40fc553725 100644 --- a/apps/encryption/tests/UtilTest.php +++ b/apps/encryption/tests/UtilTest.php @@ -152,7 +152,7 @@ class UtilTest extends TestCase { */ public function testIsMasterKeyEnabled($value, $expect) { $this->configMock->expects($this->once())->method('getAppValue') - ->with('encryption', 'useMasterKey', '0')->willReturn($value); + ->with('encryption', 'useMasterKey', '1')->willReturn($value); $this->assertSame($expect, $this->instance->isMasterKeyEnabled() ); diff --git a/apps/files_sharing/tests/EncryptedSizePropagationTest.php b/apps/files_sharing/tests/EncryptedSizePropagationTest.php index 6b6ed2cd73..38bbf12177 100644 --- a/apps/files_sharing/tests/EncryptedSizePropagationTest.php +++ b/apps/files_sharing/tests/EncryptedSizePropagationTest.php @@ -36,8 +36,10 @@ class EncryptedSizePropagationTest extends SizePropagationTest { $this->createUser($name, $password); $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); + $this->config->setAppValue('encryption', 'useMasterKey', '0'); $this->setupForUser($name, $password); $this->loginWithEncryption($name); return new View('/' . $name . '/files'); } + } diff --git a/lib/private/Encryption/Update.php b/lib/private/Encryption/Update.php index ad40183767..94d64b7350 100644 --- a/lib/private/Encryption/Update.php +++ b/lib/private/Encryption/Update.php @@ -168,6 +168,14 @@ class Update { */ public function update($path) { + $encryptionModule = $this->encryptionManager->getEncryptionModule(); + + // if the encryption module doesn't encrypt the files on a per-user basis + // we have nothing to do here. + if ($encryptionModule->needDetailedAccessList() === false) { + return; + } + // if a folder was shared, get a list of all (sub-)folders if ($this->view->is_dir($path)) { $allFiles = $this->util->getAllFiles($path); @@ -175,7 +183,7 @@ class Update { $allFiles = array($path); } - $encryptionModule = $this->encryptionManager->getEncryptionModule(); + foreach ($allFiles as $file) { $usersSharing = $this->file->getAccessList($file); diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php index d1f6869684..b68917ce76 100644 --- a/lib/private/Files/Stream/Encryption.php +++ b/lib/private/Files/Stream/Encryption.php @@ -254,7 +254,10 @@ class Encryption extends Wrapper { $sharePath = dirname($sharePath); } - $accessList = $this->file->getAccessList($sharePath); + $accessList = []; + if ($this->encryptionModule->needDetailedAccessList()) { + $accessList = $this->file->getAccessList($sharePath); + } $this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $mode, $this->header, $accessList); if ( diff --git a/lib/public/Encryption/IEncryptionModule.php b/lib/public/Encryption/IEncryptionModule.php index 6be9763c9c..d96c6c8ba0 100644 --- a/lib/public/Encryption/IEncryptionModule.php +++ b/lib/public/Encryption/IEncryptionModule.php @@ -182,4 +182,14 @@ interface IEncryptionModule { */ public function isReadyForUser($user); + /** + * Does the encryption module needs a detailed list of users with access to the file? + * For example if the encryption module uses per-user encryption keys and needs to know + * the users with access to the file to encrypt/decrypt it. + * + * @since 13.0.0 + * @return bool + */ + public function needDetailedAccessList(); + } diff --git a/settings/Controller/UsersController.php b/settings/Controller/UsersController.php index a193f9bc8d..76394fcb6c 100644 --- a/settings/Controller/UsersController.php +++ b/settings/Controller/UsersController.php @@ -42,6 +42,8 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\Files\Config\IUserMountCache; +use OCP\Encryption\IEncryptionModule; +use OCP\Encryption\IManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; @@ -99,9 +101,14 @@ class UsersController extends Controller { private $keyManager; /** @var IJobList */ private $jobList; + /** @var IUserMountCache */ private $userMountCache; + /** @var IManager */ + private $encryptionManager; + + /** * @param string $appName * @param IRequest $request @@ -124,6 +131,7 @@ class UsersController extends Controller { * @param Manager $keyManager * @param IJobList $jobList * @param IUserMountCache $userMountCache + * @param IManager $encryptionManager */ public function __construct($appName, IRequest $request, @@ -145,7 +153,8 @@ class UsersController extends Controller { ICrypto $crypto, Manager $keyManager, IJobList $jobList, - IUserMountCache $userMountCache) { + IUserMountCache $userMountCache, + IManager $encryptionManager) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->groupManager = $groupManager; @@ -165,6 +174,7 @@ class UsersController extends Controller { $this->keyManager = $keyManager; $this->jobList = $jobList; $this->userMountCache = $userMountCache; + $this->encryptionManager = $encryptionManager; // check for encryption state - TODO see formatUserForIndex $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption'); @@ -200,6 +210,17 @@ class UsersController extends Controller { // user also has recovery mode enabled $restorePossible = true; } + } else { + $modules = $this->encryptionManager->getEncryptionModules(); + $restorePossible = true; + foreach ($modules as $id => $module) { + /* @var IEncryptionModule $instance */ + $instance = call_user_func($module['callback']); + if ($instance->needDetailedAccessList()) { + $restorePossible = false; + break; + } + } } } else { // recovery is possible if encryption is disabled (plain files are diff --git a/tests/Settings/Controller/UsersControllerTest.php b/tests/Settings/Controller/UsersControllerTest.php index 0780f5219c..cd08c83414 100644 --- a/tests/Settings/Controller/UsersControllerTest.php +++ b/tests/Settings/Controller/UsersControllerTest.php @@ -20,6 +20,8 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\Files\Config\IUserMountCache; +use OCP\Encryption\IEncryptionModule; +use OCP\Encryption\IManager; use OCP\IAvatar; use OCP\IAvatarManager; use OCP\IConfig; @@ -82,6 +84,10 @@ class UsersControllerTest extends \Test\TestCase { private $securityManager; /** @var IUserMountCache |\PHPUnit_Framework_MockObject_MockObject */ private $userMountCache; + /** @var IManager | \PHPUnit_Framework_MockObject_MockObject */ + private $encryptionManager; + /** @var IEncryptionModule | \PHPUnit_Framework_MockObject_MockObject */ + private $encryptionModule; protected function setUp() { parent::setUp(); @@ -104,6 +110,7 @@ class UsersControllerTest extends \Test\TestCase { $this->crypto = $this->createMock(ICrypto::class); $this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock(); $this->jobList = $this->createMock(IJobList::class); + $this->encryptionManager = $this->createMock(IManager::class); $this->l = $this->createMock(IL10N::class); $this->l->method('t') ->will($this->returnCallback(function ($text, $parameters = []) { @@ -111,6 +118,10 @@ class UsersControllerTest extends \Test\TestCase { })); $this->userMountCache = $this->createMock(IUserMountCache::class); + $this->encryptionModule = $this->createMock(IEncryptionModule::class); + $this->encryptionManager->expects($this->any())->method('getEncryptionModules') + ->willReturn(['encryptionModule' => ['callback' => function() { return $this->encryptionModule;}]]); + /* * Set default avatar behaviour for whole test suite */ @@ -154,8 +165,8 @@ class UsersControllerTest extends \Test\TestCase { $this->crypto, $this->securityManager, $this->jobList, - $this->userMountCache - + $this->userMountCache, + $this->encryptionManager ); } else { return $this->getMockBuilder(UsersController::class) @@ -182,6 +193,7 @@ class UsersControllerTest extends \Test\TestCase { $this->securityManager, $this->jobList, $this->userMountCache, + $this->encryptionManager ] )->setMethods($mockedMethods)->getMock(); } @@ -1689,9 +1701,17 @@ class UsersControllerTest extends \Test\TestCase { $this->assertEquals($expectedResult, $result); } - public function testRestoreNotPossibleWithoutAdminRestore() { + /** + * @dataProvider dataTestRestoreNotPossibleWithoutAdminRestore + * + * @param bool $masterKeyEnabled + */ + public function testRestoreNotPossibleWithoutAdminRestore($masterKeyEnabled) { list($user, $expectedResult) = $this->mockUser(); + // without the master key enabled we use per-user keys + $this->encryptionModule->expects($this->once())->method('needDetailedAccessList')->willReturn(!$masterKeyEnabled); + $this->appManager ->method('isEnabledForUser') ->with( @@ -1699,7 +1719,8 @@ class UsersControllerTest extends \Test\TestCase { ) ->will($this->returnValue(true)); - $expectedResult['isRestoreDisabled'] = true; + // without the master key enabled we use per-user keys -> restore is disabled + $expectedResult['isRestoreDisabled'] = !$masterKeyEnabled; $subadmin = $this->getMockBuilder('\OC\SubAdmin') ->disableOriginalConstructor() @@ -1718,6 +1739,13 @@ class UsersControllerTest extends \Test\TestCase { $this->assertEquals($expectedResult, $result); } + public function dataTestRestoreNotPossibleWithoutAdminRestore() { + return [ + [true], + [false] + ]; + } + public function testRestoreNotPossibleWithoutUserRestore() { list($user, $expectedResult) = $this->mockUser(); diff --git a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php index d310f110b9..a66ff14a77 100644 --- a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php +++ b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php @@ -212,7 +212,7 @@ class EncryptionTest extends Storage { protected function buildMockModule() { $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser']) + ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) ->getMock(); $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); @@ -225,6 +225,7 @@ class EncryptionTest extends Storage { $this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true); $this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192); $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true); + $this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false); return $this->encryptionModule; } diff --git a/tests/lib/Files/Stream/EncryptionTest.php b/tests/lib/Files/Stream/EncryptionTest.php index e072dd6718..1dc9dca0aa 100644 --- a/tests/lib/Files/Stream/EncryptionTest.php +++ b/tests/lib/Files/Stream/EncryptionTest.php @@ -58,7 +58,8 @@ class EncryptionTest extends \Test\TestCase { /** * @dataProvider dataProviderStreamOpen() */ - public function testStreamOpen($mode, + public function testStreamOpen($isMasterKeyUsed, + $mode, $fullPath, $fileExists, $expectedSharePath, @@ -69,6 +70,7 @@ class EncryptionTest extends \Test\TestCase { // build mocks $encryptionModuleMock = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor()->getMock(); + $encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed); $encryptionModuleMock->expects($this->once()) ->method('getUnencryptedBlockSize')->willReturn(99); $encryptionModuleMock->expects($this->once()) @@ -80,12 +82,15 @@ class EncryptionTest extends \Test\TestCase { $fileMock = $this->getMockBuilder('\OC\Encryption\File') ->disableOriginalConstructor()->getMock(); - $fileMock->expects($this->once())->method('getAccessList') - ->will($this->returnCallback(function($sharePath) use ($expectedSharePath) { - $this->assertSame($expectedSharePath, $sharePath); - return array(); - })); - + if ($isMasterKeyUsed) { + $fileMock->expects($this->never())->method('getAccessList'); + } else { + $fileMock->expects($this->once())->method('getAccessList') + ->will($this->returnCallback(function ($sharePath) use ($expectedSharePath) { + $this->assertSame($expectedSharePath, $sharePath); + return array(); + })); + } $utilMock = $this->getMockBuilder('\OC\Encryption\Util') ->disableOriginalConstructor()->getMock(); $utilMock->expects($this->any()) @@ -152,11 +157,14 @@ class EncryptionTest extends \Test\TestCase { } public function dataProviderStreamOpen() { - return array( - array('r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true), - array('r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true), - array('w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false), - ); + return [ + [false, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true], + [false, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true], + [false, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false], + [true, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true], + [true, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true], + [true, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false], + ]; } public function testWriteRead() { @@ -193,7 +201,7 @@ class EncryptionTest extends \Test\TestCase { $stream = $this->getStream($fileName, 'r', 6); $this->assertEquals('barbar', fread($stream, 100)); fclose($stream); - + unlink($fileName); } @@ -311,7 +319,7 @@ class EncryptionTest extends \Test\TestCase { protected function buildMockModule() { $encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser']) + ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) ->getMock(); $encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); @@ -319,6 +327,7 @@ class EncryptionTest extends \Test\TestCase { $encryptionModule->expects($this->any())->method('begin')->willReturn([]); $encryptionModule->expects($this->any())->method('end')->willReturn(''); $encryptionModule->expects($this->any())->method('isReadable')->willReturn(true); + $encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false); $encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function($data) { // simulate different block size by adding some padding to the data if (isset($data[6125])) { diff --git a/tests/lib/Traits/EncryptionTrait.php b/tests/lib/Traits/EncryptionTrait.php index 5e2ca4e561..8a06d37fa7 100644 --- a/tests/lib/Traits/EncryptionTrait.php +++ b/tests/lib/Traits/EncryptionTrait.php @@ -64,6 +64,7 @@ trait EncryptionTrait { /** @var Setup $userSetup */ $userSetup = $container->query('UserSetup'); $userSetup->setupUser($name, $password); + $this->encryptionApp->setUp(); $keyManager->init($name, $password); } @@ -99,6 +100,7 @@ trait EncryptionTrait { if ($this->config) { $this->config->setAppValue('core', 'encryption_enabled', $this->encryptionWasEnabled); $this->config->setAppValue('core', 'default_encryption_module', $this->originalEncryptionModule); + $this->config->deleteAppValue('encryption', 'useMasterKey'); } } }