occ script to disable encryption and to decrypt all files again

This commit is contained in:
Bjoern Schiessle 2015-08-24 12:03:53 +02:00
parent 230029e509
commit 9bd4f2d41e
15 changed files with 1482 additions and 4 deletions

View File

@ -30,6 +30,7 @@ use OCA\Encryption\Controller\RecoveryController;
use OCA\Encryption\Controller\SettingsController;
use OCA\Encryption\Controller\StatusController;
use OCA\Encryption\Crypto\Crypt;
use OCA\Encryption\Crypto\DecryptAll;
use OCA\Encryption\Crypto\EncryptAll;
use OCA\Encryption\Crypto\Encryption;
use OCA\Encryption\HookManager;
@ -113,7 +114,9 @@ class Application extends \OCP\AppFramework\App {
$container->query('Crypt'),
$container->query('KeyManager'),
$container->query('Util'),
$container->query('Session'),
$container->query('EncryptAll'),
$container->query('DecryptAll'),
$container->getServer()->getLogger(),
$container->getServer()->getL10N($container->getAppName())
);
@ -242,6 +245,18 @@ class Application extends \OCP\AppFramework\App {
}
);
$container->registerService('DecryptAll',
function (IAppContainer $c) {
return new DecryptAll(
$c->query('Util'),
$c->query('KeyManager'),
$c->query('Crypt'),
$c->query('Session'),
new QuestionHelper()
);
}
);
}
public function registerSettings() {

View File

@ -0,0 +1,143 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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\Crypto;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Session;
use OCA\Encryption\Util;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
class DecryptAll {
/** @var Util */
protected $util;
/** @var QuestionHelper */
protected $questionHelper;
/** @var Crypt */
protected $crypt;
/** @var KeyManager */
protected $keyManager;
/** @var Session */
protected $session;
/**
* @param Util $util
* @param KeyManager $keyManager
* @param Crypt $crypt
* @param Session $session
* @param QuestionHelper $questionHelper
*/
public function __construct(
Util $util,
KeyManager $keyManager,
Crypt $crypt,
Session $session,
QuestionHelper $questionHelper
) {
$this->util = $util;
$this->keyManager = $keyManager;
$this->crypt = $crypt;
$this->session = $session;
$this->questionHelper = $questionHelper;
}
/**
* prepare encryption module to decrypt all files
*
* @param InputInterface $input
* @param OutputInterface $output
* @param $user
* @return bool
*/
public function prepare(InputInterface $input, OutputInterface $output, $user) {
$question = new Question('Please enter the recovery key password: ');
$recoveryKeyId = $this->keyManager->getRecoveryKeyId();
if (!empty($user)) {
$questionUseLoginPassword = new ConfirmationQuestion(
'Do you want to use the users login password to decrypt all files? (y/n) ',
false
);
$useLoginPassword = $this->questionHelper->ask($input, $output, $questionUseLoginPassword);
if ($useLoginPassword) {
$question = new Question('Please enter the users login password: ');
} else if ($this->util->isRecoveryEnabledForUser($user) === false) {
$output->writeln('No recovery key available for user ' . $user);
return false;
} else {
$user = $recoveryKeyId;
}
} else {
$user = $recoveryKeyId;
}
$question->setHidden(true);
$question->setHiddenFallback(false);
$password = $this->questionHelper->ask($input, $output, $question);
$privateKey = $this->getPrivateKey($user, $password);
if ($privateKey !== false) {
$this->updateSession($user, $privateKey);
return true;
} else {
$output->writeln('Could not decrypt private key, maybe you entered the wrong password?');
}
return false;
}
/**
* get the private key which will be used to decrypt all files
*
* @param string $user
* @param string $password
* @return bool|string
* @throws \OCA\Encryption\Exceptions\PrivateKeyMissingException
*/
protected function getPrivateKey($user, $password) {
$recoveryKeyId = $this->keyManager->getRecoveryKeyId();
if ($user === $recoveryKeyId) {
$recoveryKey = $this->keyManager->getSystemPrivateKey($recoveryKeyId);
$privateKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
} else {
$userKey = $this->keyManager->getPrivateKey($user);
$privateKey = $this->crypt->decryptPrivateKey($userKey, $password, $user);
}
return $privateKey;
}
protected function updateSession($user, $privateKey) {
$this->session->prepareDecryptAll($user, $privateKey);
}
}

View File

@ -30,6 +30,7 @@ namespace OCA\Encryption\Crypto;
use OC\Encryption\Exceptions\DecryptionFailedException;
use OCA\Encryption\Exceptions\PublicKeyMissingException;
use OCA\Encryption\Session;
use OCA\Encryption\Util;
use OCP\Encryption\IEncryptionModule;
use OCA\Encryption\KeyManager;
@ -75,6 +76,9 @@ class Encryption implements IEncryptionModule {
/** @var Util */
private $util;
/** @var Session */
private $session;
/** @var ILogger */
private $logger;
@ -87,25 +91,34 @@ class Encryption implements IEncryptionModule {
/** @var bool */
private $useMasterPassword;
/** @var DecryptAll */
private $decryptAll;
/**
*
* @param Crypt $crypt
* @param KeyManager $keyManager
* @param Util $util
* @param Session $session
* @param EncryptAll $encryptAll
* @param DecryptAll $decryptAll
* @param ILogger $logger
* @param IL10N $il10n
*/
public function __construct(Crypt $crypt,
KeyManager $keyManager,
Util $util,
Session $session,
EncryptAll $encryptAll,
DecryptAll $decryptAll,
ILogger $logger,
IL10N $il10n) {
$this->crypt = $crypt;
$this->keyManager = $keyManager;
$this->util = $util;
$this->session = $session;
$this->encryptAll = $encryptAll;
$this->decryptAll = $decryptAll;
$this->logger = $logger;
$this->l = $il10n;
$this->useMasterPassword = $util->isMasterKeyEnabled();
@ -150,7 +163,15 @@ class Encryption implements IEncryptionModule {
$this->isWriteOperation = false;
$this->writeCache = '';
$this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
if ($this->session->decryptAllModeActivated()) {
$encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path);
$shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid());
$this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
$shareKey,
$this->session->getDecryptAllKey());
} else {
$this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
}
if (
$mode === 'w'
@ -426,6 +447,19 @@ class Encryption implements IEncryptionModule {
$this->encryptAll->encryptAll($input, $output);
}
/**
* prepare module to perform decrypt all operation
*
* @param InputInterface $input
* @param OutputInterface $output
* @param string $user
* @return bool
*/
public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '') {
return $this->decryptAll->prepare($input, $output, $user);
}
/**
* @param string $path
* @return string

View File

@ -25,6 +25,7 @@
namespace OCA\Encryption;
use OCA\Encryption\Exceptions\PrivateKeyMissingException;
use \OCP\ISession;
class Session {
@ -106,6 +107,61 @@ class Session {
$this->session->set('privateKey', $key);
}
/**
* store data needed for the decrypt all operation in the session
*
* @param string $user
* @param string $key
*/
public function prepareDecryptAll($user, $key) {
$this->session->set('decryptAll', true);
$this->session->set('decryptAllKey', $key);
$this->session->set('decryptAllUid', $user);
}
/**
* check if we are in decrypt all mode
*
* @return bool
*/
public function decryptAllModeActivated() {
$decryptAll = $this->session->get('decryptAll');
return ($decryptAll === true);
}
/**
* get uid used for decrypt all operation
*
* @return string
* @throws \Exception
*/
public function getDecryptAllUid() {
$uid = $this->session->get('decryptAllUid');
if (is_null($uid) && $this->decryptAllModeActivated()) {
throw new \Exception('No uid found while in decrypt all mode');
} elseif (is_null($uid)) {
throw new \Exception('Please activate decrypt all mode first');
}
return $uid;
}
/**
* get private key for decrypt all operation
*
* @return string
* @throws PrivateKeyMissingException
*/
public function getDecryptAllKey() {
$privateKey = $this->session->get('decryptAllKey');
if (is_null($privateKey) && $this->decryptAllModeActivated()) {
throw new PrivateKeyMissingException('No private key found while in decrypt all mode');
} elseif (is_null($privateKey)) {
throw new PrivateKeyMissingException('Please activate decrypt all mode first');
}
return $privateKey;
}
/**
* remove keys from session
@ -114,7 +170,9 @@ class Session {
$this->session->remove('publicSharePrivateKey');
$this->session->remove('privateKey');
$this->session->remove('encryptionInitialized');
$this->session->remove('decryptAll');
$this->session->remove('decryptAllKey');
$this->session->remove('decryptAllUid');
}
}

View File

@ -56,6 +56,7 @@ class SessionTest extends TestCase {
* @depends testSetAndGetPrivateKey
*/
public function testIsPrivateKeySet() {
$this->instance->setPrivateKey('dummyPrivateKey');
$this->assertTrue($this->instance->isPrivateKeySet());
unset(self::$tempStorage['privateKey']);
@ -65,6 +66,51 @@ class SessionTest extends TestCase {
self::$tempStorage['privateKey'] = 'dummyPrivateKey';
}
public function testDecryptAllModeActivated() {
$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() {
$this->assertFalse($this->instance->decryptAllModeActivated());
}
/**
* @expectedException \Exception
* @expectExceptionMessage 'Please activate decrypt all mode first'
*/
public function testGetDecryptAllUidException() {
$this->instance->getDecryptAllUid();
}
/**
* @expectedException \Exception
* @expectExceptionMessage 'No uid found while in decrypt all mode'
*/
public function testGetDecryptAllUidException2() {
$this->instance->prepareDecryptAll(null, 'key');
$this->instance->getDecryptAllUid();
}
/**
* @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
* @expectExceptionMessage 'Please activate decrypt all mode first'
*/
public function testGetDecryptAllKeyException() {
$this->instance->getDecryptAllKey();
}
/**
* @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
* @expectExceptionMessage 'No key found while in decrypt all mode'
*/
public function testGetDecryptAllKeyException2() {
$this->instance->prepareDecryptAll('user', null);
$this->instance->getDecryptAllKey();
}
/**
*
*/
@ -112,6 +158,10 @@ class SessionTest extends TestCase {
*
*/
public function testClearWillRemoveValues() {
$this->instance->setPrivateKey('privateKey');
$this->instance->setStatus('initStatus');
$this->instance->prepareDecryptAll('user', 'key');
$this->assertNotEmpty(self::$tempStorage);
$this->instance->clear();
$this->assertEmpty(self::$tempStorage);
}
@ -138,4 +188,9 @@ class SessionTest extends TestCase {
$this->instance = new Session($this->sessionMock);
}
protected function tearDown() {
self::$tempStorage = [];
parent::tearDown();
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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\Crypt;
use OCA\Encryption\Crypto\DecryptAll;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Session;
use OCA\Encryption\Util;
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;
/** @var KeyManager | \PHPUnit_Framework_MockObject_MockObject */
protected $keyManager;
/** @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() {
parent::setUp();
$this->util = $this->getMockBuilder('OCA\Encryption\Util')
->disableOriginalConstructor()->getMock();
$this->keyManager = $this->getMockBuilder('OCA\Encryption\KeyManager')
->disableOriginalConstructor()->getMock();
$this->crypt = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
->disableOriginalConstructor()->getMock();
$this->session = $this->getMockBuilder('OCA\Encryption\Session')
->disableOriginalConstructor()->getMock();
$this->questionHelper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper')
->disableOriginalConstructor()->getMock();
$this->instance = new DecryptAll(
$this->util,
$this->keyManager,
$this->crypt,
$this->session,
$this->questionHelper
);
}
public function testUpdateSession() {
$this->session->expects($this->once())->method('prepareDecryptAll')
->with('user1', 'key1');
$this->invokePrivate($this->instance, 'updateSession', ['user1', 'key1']);
}
/**
* @dataProvider dataTestGetPrivateKey
*
* @param string $user
* @param string $recoveryKeyId
*/
public function testGetPrivateKey($user, $recoveryKeyId) {
$password = 'passwd';
$recoveryKey = 'recoveryKey';
$userKey = 'userKey';
$unencryptedKey = 'unencryptedKey';
$this->keyManager->expects($this->any())->method('getRecoveryKeyId')
->willReturn($recoveryKeyId);
if ($user === $recoveryKeyId) {
$this->keyManager->expects($this->once())->method('getSystemPrivateKey')
->with($recoveryKeyId)->willReturn($recoveryKey);
$this->keyManager->expects($this->never())->method('getPrivateKey');
$this->crypt->expects($this->once())->method('decryptPrivateKey')
->with($recoveryKey, $password)->willReturn($unencryptedKey);
} else {
$this->keyManager->expects($this->never())->method('getSystemPrivateKey');
$this->keyManager->expects($this->once())->method('getPrivateKey')
->with($user)->willReturn($userKey);
$this->crypt->expects($this->once())->method('decryptPrivateKey')
->with($userKey, $password, $user)->willReturn($unencryptedKey);
}
$this->assertSame($unencryptedKey,
$this->invokePrivate($this->instance, 'getPrivateKey', [$user, $password])
);
}
public function dataTestGetPrivateKey() {
return [
['user1', 'recoveryKey'],
['recoveryKeyId', 'recoveryKeyId']
];
}
}

View File

@ -39,6 +39,12 @@ class EncryptionTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $encryptAllMock;
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $decryptAllMock;
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $sessionMock;
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $cryptMock;
@ -63,9 +69,15 @@ class EncryptionTest extends TestCase {
$this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')
->disableOriginalConstructor()
->getMock();
$this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session')
->disableOriginalConstructor()
->getMock();
$this->encryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll')
->disableOriginalConstructor()
->getMock();
$this->decryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\DecryptAll')
->disableOriginalConstructor()
->getMock();
$this->loggerMock = $this->getMockBuilder('OCP\ILogger')
->disableOriginalConstructor()
->getMock();
@ -81,7 +93,9 @@ class EncryptionTest extends TestCase {
$this->cryptMock,
$this->keyManagerMock,
$this->utilMock,
$this->sessionMock,
$this->encryptAllMock,
$this->decryptAllMock,
$this->loggerMock,
$this->l10nMock
);
@ -170,6 +184,16 @@ class EncryptionTest extends TestCase {
*/
public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected) {
$this->sessionMock->expects($this->once())
->method('decryptAllModeActivated')
->willReturn(false);
$this->sessionMock->expects($this->never())->method('getDecryptAllUid');
$this->sessionMock->expects($this->never())->method('getDecryptAllKey');
$this->keyManagerMock->expects($this->never())->method('getEncryptedFileKey');
$this->keyManagerMock->expects($this->never())->method('getShareKey');
$this->cryptMock->expects($this->never())->method('multiKeyDecrypt');
$this->cryptMock->expects($this->any())
->method('getCipher')
->willReturn($defaultCipher);
@ -209,6 +233,49 @@ class EncryptionTest extends TestCase {
);
}
/**
* test begin() if decryptAll mode was activated
*/
public function testBeginDecryptAll() {
$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)
->willReturn($fileKey);
$this->keyManagerMock->expects($this->never())->method('getFileKey');
$this->instance->begin($path, 'user', 'r', [], []);
$this->assertSame($fileKey,
$this->invokePrivate($this->instance, 'fileKey')
);
}
/**
* @dataProvider dataTestUpdate
*
@ -273,4 +340,15 @@ class EncryptionTest extends TestCase {
public function testDecrypt() {
$this->instance->decrypt('abc');
}
public function testPrepareDecryptAll() {
$input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$this->decryptAllMock->expects($this->once())->method('prepare')
->with($input, $output, 'user');
$this->instance->prepareDecryptAll($input, $output, 'user');
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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 OC\Core\Command\Encryption;
use OCP\App\IAppManager;
use OCP\Encryption\IManager;
use OCP\IConfig;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class DecryptAll extends Command {
/** @var IManager */
protected $encryptionManager;
/** @var IAppManager */
protected $appManager;
/** @var IConfig */
protected $config;
/** @var QuestionHelper */
protected $questionHelper;
/** @var bool */
protected $wasTrashbinEnabled;
/** @var bool */
protected $wasSingleUserModeEnabled;
/** @var \OC\Encryption\DecryptAll */
protected $decryptAll;
/**
* @param IManager $encryptionManager
* @param IAppManager $appManager
* @param IConfig $config
* @param \OC\Encryption\DecryptAll $decryptAll
* @param QuestionHelper $questionHelper
*/
public function __construct(
IManager $encryptionManager,
IAppManager $appManager,
IConfig $config,
\OC\Encryption\DecryptAll $decryptAll,
QuestionHelper $questionHelper
) {
parent::__construct();
$this->appManager = $appManager;
$this->encryptionManager = $encryptionManager;
$this->config = $config;
$this->decryptAll = $decryptAll;
$this->questionHelper = $questionHelper;
$this->wasTrashbinEnabled = $this->appManager->isEnabledForUser('files_trashbin');
$this->wasSingleUserModeEnabled = $this->config->getSystemValue('singleUser', false);
$this->config->setSystemValue('singleUser', true);
$this->appManager->disableApp('files_trashbin');
}
public function __destruct() {
$this->config->setSystemValue('singleUser', $this->wasSingleUserModeEnabled);
if ($this->wasTrashbinEnabled) {
$this->appManager->enableApp('files_trashbin');
}
}
protected function configure() {
parent::configure();
$this->setName('encryption:decrypt-all');
$this->setDescription(
'This will disable server-side encryption and decrypt all files for '
. 'all users if it is supported by your encryption module. '
. 'Please make sure that no user access his files during this process!'
);
$this->addArgument(
'user',
InputArgument::OPTIONAL,
'user for which you want to decrypt all files (optional)'
);
}
protected function execute(InputInterface $input, OutputInterface $output) {
try {
if ($this->encryptionManager->isEnabled() === true) {
$output->write('Disable server side encryption... ');
$this->config->setAppValue('core', 'encryption_enabled', 'no');
$output->writeln('done.');
} else {
$output->writeln('Server side encryption not enabled. Nothing to do.');
return;
}
$output->writeln("\n");
$output->writeln('You are about to start to decrypt all files stored in your ownCloud.');
$output->writeln('It will depend on the encryption module and your setup if this is possible.');
$output->writeln('Depending on the number and size of your files this can take some time');
$output->writeln('Please make sure that no user access his files during this process!');
$output->writeln('');
$question = new ConfirmationQuestion('Do you really want to continue? (y/n) ', false);
if ($this->questionHelper->ask($input, $output, $question)) {
$user = $input->getArgument('user');
$result = $this->decryptAll->decryptAll($input, $output, $user);
if ($result === false) {
$this->output->writeln(' aborted.');
$this->config->setAppValue('core', 'encryption_enabled', 'yes');
}
} else {
$output->write('Enable server side encryption... ');
$this->config->setAppValue('core', 'encryption_enabled', 'yes');
$output->writeln('done.');
$output->writeln('aborted');
}
} catch (\Exception $e) {
// enable server side encryption again if something went wrong
$this->config->setAppValue('core', 'encryption_enabled', 'yes');
throw $e;
}
}
}

View File

@ -58,6 +58,13 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\Encryption\SetDefaultModule(\OC::$server->getEncryptionManager()));
$application->add(new OC\Core\Command\Encryption\Status(\OC::$server->getEncryptionManager()));
$application->add(new OC\Core\Command\Encryption\EncryptAll(\OC::$server->getEncryptionManager(), \OC::$server->getAppManager(), \OC::$server->getConfig(), new \Symfony\Component\Console\Helper\QuestionHelper()));
$application->add(new OC\Core\Command\Encryption\DecryptAll(
\OC::$server->getEncryptionManager(),
\OC::$server->getAppManager(),
\OC::$server->getConfig(),
new \OC\Encryption\DecryptAll(\OC::$server->getEncryptionManager(), \OC::$server->getUserManager(), new \OC\Files\View()),
new \Symfony\Component\Console\Helper\QuestionHelper())
);
$application->add(new OC\Core\Command\Log\Manage(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Log\OwnCloud(\OC::$server->getConfig()));

View File

@ -0,0 +1,268 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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 OC\Encryption;
use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Files\View;
use \OCP\Encryption\IEncryptionModule;
use OCP\IUserManager;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DecryptAll {
/** @var OutputInterface */
protected $output;
/** @var InputInterface */
protected $input;
/** @var Manager */
protected $encryptionManager;
/** @var IUserManager */
protected $userManager;
/** @var View */
protected $rootView;
/** @var array files which couldn't be decrypted */
protected $failed;
/**
* @param Manager $encryptionManager
* @param IUserManager $userManager
* @param View $rootView
*/
public function __construct(
Manager $encryptionManager,
IUserManager $userManager,
View $rootView
) {
$this->encryptionManager = $encryptionManager;
$this->userManager = $userManager;
$this->rootView = $rootView;
$this->failed = [];
}
/**
* start to decrypt all files
*
* @param InputInterface $input
* @param OutputInterface $output
* @param string $user which users data folder should be decrypted, default = all users
* @return bool
* @throws \Exception
*/
public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') {
$this->input = $input;
$this->output = $output;
$this->output->writeln('prepare encryption modules...');
if ($this->prepareEncryptionModules($user) === false) {
return false;
}
$this->output->writeln(' done.');
$this->decryptAllUsersFiles($user);
if (empty($this->failed)) {
$this->output->writeln('all files could be decrypted successfully!');
} else {
$this->output->writeln('Files for following users couldn\'t be decrypted, ');
$this->output->writeln('maybe the user is not set up in a way that supports this operation: ');
foreach ($this->failed as $uid => $paths) {
$this->output->writeln(' ' . $uid);
}
$this->output->writeln('');
}
return true;
}
/**
* prepare encryption modules to perform the decrypt all function
*
* @param $user
* @return bool
*/
protected function prepareEncryptionModules($user) {
// prepare all encryption modules for decrypt all
$encryptionModules = $this->encryptionManager->getEncryptionModules();
foreach ($encryptionModules as $moduleDesc) {
/** @var IEncryptionModule $module */
$module = call_user_func($moduleDesc['callback']);
if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) {
$this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!');
return false;
}
}
return true;
}
/**
* iterate over all user and encrypt their files
* @param string $user which users files should be decrypted, default = all users
*/
protected function decryptAllUsersFiles($user = '') {
$this->output->writeln("\n");
$userList = [];
if (empty($user)) {
$fetchUsersProgress = new ProgressBar($this->output);
$fetchUsersProgress->setFormat(" %message% \n [%bar%]");
$fetchUsersProgress->start();
$fetchUsersProgress->setMessage("Fetch list of users...");
$fetchUsersProgress->advance();
foreach ($this->userManager->getBackends() as $backend) {
$limit = 500;
$offset = 0;
do {
$users = $backend->getUsers('', $limit, $offset);
foreach ($users as $user) {
$userList[] = $user;
}
$offset += $limit;
$fetchUsersProgress->advance();
} while (count($users) >= $limit);
$fetchUsersProgress->setMessage("Fetch list of users... finished");
$fetchUsersProgress->finish();
}
} else {
$userList[] = $user;
}
$this->output->writeln("\n\n");
$progress = new ProgressBar($this->output);
$progress->setFormat(" %message% \n [%bar%]");
$progress->start();
$progress->setMessage("starting to decrypt files...");
$progress->advance();
$numberOfUsers = count($userList);
$userNo = 1;
foreach ($userList as $uid) {
$userCount = "$uid ($userNo of $numberOfUsers)";
$this->decryptUsersFiles($uid, $progress, $userCount);
$userNo++;
}
$progress->setMessage("starting to decrypt files... finished");
$progress->finish();
$this->output->writeln("\n\n");
}
/**
* encrypt files from the given user
*
* @param string $uid
* @param ProgressBar $progress
* @param string $userCount
*/
protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) {
$this->setupUserFS($uid);
$directories = array();
$directories[] = '/' . $uid . '/files';
while($root = array_pop($directories)) {
$content = $this->rootView->getDirectoryContent($root);
foreach ($content as $file) {
$path = $root . '/' . $file['name'];
if ($this->rootView->is_dir($path)) {
$directories[] = $path;
continue;
} else {
try {
$progress->setMessage("decrypt files for user $userCount: $path");
$progress->advance();
if ($this->decryptFile($path) === false) {
$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
$progress->advance();
}
} catch (\Exception $e) {
if (isset($this->failed[$uid])) {
$this->failed[$uid][] = $path;
} else {
$this->failed[$uid] = [$path];
}
}
}
}
}
}
/**
* encrypt file
*
* @param string $path
* @return bool
*/
protected function decryptFile($path) {
$source = $path;
$target = $path . '.decrypted.' . $this->getTimestamp();
try {
$this->rootView->copy($source, $target);
$this->rootView->rename($target, $source);
} catch (DecryptionFailedException $e) {
if ($this->rootView->file_exists($target)) {
$this->rootView->unlink($target);
}
return false;
}
return true;
}
/**
* get current timestamp
*
* @return int
*/
protected function getTimestamp() {
return time();
}
/**
* setup user file system
*
* @param string $uid
*/
protected function setupUserFS($uid) {
\OC_Util::tearDownFS();
\OC_Util::setupFS($uid);
}
}

View File

@ -145,4 +145,15 @@ interface IEncryptionModule {
*/
public function encryptAll(InputInterface $input, OutputInterface $output);
/**
* prepare encryption module to decrypt all files
*
* @param InputInterface $input
* @param OutputInterface $output write some status information to the terminal during encryption
* @param $user (optional) for which the files should be decrypted, default = all users
* @return bool return false on failure or if it isn't supported by the module
* @since 8.2.0
*/
public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '');
}

View File

@ -0,0 +1,215 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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 Tests\Core\Command\Encryption;
use OC\Core\Command\Encryption\DecryptAll;
use Test\TestCase;
class DecryptAllTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IConfig */
protected $config;
/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\Encryption\IManager */
protected $encryptionManager;
/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\App\IAppManager */
protected $appManager;
/** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Input\InputInterface */
protected $consoleInput;
/** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Output\OutputInterface */
protected $consoleOutput;
/** @var \PHPUnit_Framework_MockObject_MockObject | \Symfony\Component\Console\Helper\QuestionHelper */
protected $questionHelper;
/** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Encryption\DecryptAll */
protected $decryptAll;
public function setUp() {
parent::setUp();
$this->config = $this->getMockBuilder('OCP\IConfig')
->disableOriginalConstructor()
->getMock();
$this->encryptionManager = $this->getMockBuilder('OCP\Encryption\IManager')
->disableOriginalConstructor()
->getMock();
$this->appManager = $this->getMockBuilder('OCP\App\IAppManager')
->disableOriginalConstructor()
->getMock();
$this->questionHelper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper')
->disableOriginalConstructor()
->getMock();
$this->decryptAll = $this->getMockBuilder('OC\Encryption\DecryptAll')
->disableOriginalConstructor()->getMock();
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$this->config->expects($this->any())
->method('getSystemValue')
->with('singleUser', false)
->willReturn(false);
$this->appManager->expects($this->any())
->method('isEnabledForUser')
->with('files_trashbin')->willReturn(true);
}
public function testConstructDesctruct() {
// on construct we enable single-user-mode and disable the trash bin
$this->config->expects($this->at(1))
->method('setSystemValue')
->with('singleUser', true);
$this->appManager->expects($this->once())
->method('disableApp')
->with('files_trashbin');
// on destruct wi disable single-user-mode again and enable the trash bin
$this->config->expects($this->at(2))
->method('setSystemValue')
->with('singleUser', false);
$this->appManager->expects($this->once())
->method('enableApp')
->with('files_trashbin');
$instance = new DecryptAll(
$this->encryptionManager,
$this->appManager,
$this->config,
$this->decryptAll,
$this->questionHelper
);
$this->assertTrue(
$this->invokePrivate($instance, 'wasTrashbinEnabled')
);
$this->assertFalse(
$this->invokePrivate($instance, 'wasSingleUserModeEnabled')
);
}
/**
* @dataProvider dataTestExecute
*/
public function testExecute($encryptionEnabled, $continue) {
$instance = new DecryptAll(
$this->encryptionManager,
$this->appManager,
$this->config,
$this->decryptAll,
$this->questionHelper
);
$this->encryptionManager->expects($this->once())
->method('isEnabled')
->willReturn($encryptionEnabled);
$this->consoleInput->expects($this->any())
->method('getArgument')
->with('user')
->willReturn('user1');
if ($encryptionEnabled) {
$this->config->expects($this->at(0))
->method('setAppValue')
->with('core', 'encryption_enabled', 'no');
$this->questionHelper->expects($this->once())
->method('ask')
->willReturn($continue);
if ($continue) {
$this->decryptAll->expects($this->once())
->method('decryptAll')
->with($this->consoleInput, $this->consoleOutput, 'user1');
} else {
$this->decryptAll->expects($this->never())->method('decryptAll');
$this->config->expects($this->at(1))
->method('setAppValue')
->with('core', 'encryption_enabled', 'yes');
}
} else {
$this->config->expects($this->never())->method('setAppValue');
$this->decryptAll->expects($this->never())->method('decryptAll');
$this->questionHelper->expects($this->never())->method('ask');
}
$this->invokePrivate($instance, 'execute', [$this->consoleInput, $this->consoleOutput]);
}
public function dataTestExecute() {
return [
[true, true],
[true, false],
[false, true],
[false, false]
];
}
/**
* @expectedException \Exception
*/
public function testExecuteFailure() {
$instance = new DecryptAll(
$this->encryptionManager,
$this->appManager,
$this->config,
$this->decryptAll,
$this->questionHelper
);
$this->config->expects($this->at(0))
->method('setAppValue')
->with('core', 'encryption_enabled', 'no');
// make sure that we enable encryption again after a exception was thrown
$this->config->expects($this->at(1))
->method('setAppValue')
->with('core', 'encryption_enabled', 'yes');
$this->encryptionManager->expects($this->once())
->method('isEnabled')
->willReturn(true);
$this->consoleInput->expects($this->any())
->method('getArgument')
->with('user')
->willReturn('user1');
$this->questionHelper->expects($this->once())
->method('ask')
->willReturn(true);
$this->decryptAll->expects($this->once())
->method('decryptAll')
->with($this->consoleInput, $this->consoleOutput, 'user1')
->willReturnCallback(function() { throw new \Exception(); });
$this->invokePrivate($instance, 'execute', [$this->consoleInput, $this->consoleOutput]);
}
}

View File

@ -0,0 +1,321 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, 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 Test\Encryption;
use OC\Encryption\DecryptAll;
use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Manager;
use OC\Files\View;
use OCP\IUserManager;
use Test\TestCase;
class DecryptAllTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject | IUserManager */
protected $userManager;
/** @var \PHPUnit_Framework_MockObject_MockObject | Manager */
protected $encryptionManager;
/** @var \PHPUnit_Framework_MockObject_MockObject | View */
protected $view;
/** @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 DecryptAll */
protected $instance;
public function setUp() {
parent::setUp();
$this->userManager = $this->getMockBuilder('OCP\IUserManager')
->disableOriginalConstructor()->getMock();
$this->encryptionManager = $this->getMockBuilder('OC\Encryption\Manager')
->disableOriginalConstructor()->getMock();
$this->view = $this->getMockBuilder('OC\Files\View')
->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->instance = new DecryptAll($this->encryptionManager, $this->userManager, $this->view);
$this->invokePrivate($this->instance, 'input', [$this->inputInterface]);
$this->invokePrivate($this->instance, 'output', [$this->outputInterface]);
}
/**
* @dataProvider dataTrueFalse
*/
public function testDecryptAll($prepareResult) {
$user = 'user1';
/** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject | $instance */
$instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
->setConstructorArgs(
[
$this->encryptionManager,
$this->userManager,
$this->view
]
)
->setMethods(['prepareEncryptionModules', 'decryptAllUsersFiles'])
->getMock();
$instance->expects($this->once())
->method('prepareEncryptionModules')
->with($user)
->willReturn($prepareResult);
if ($prepareResult) {
$instance->expects($this->once())
->method('decryptAllUsersFiles')
->with($user);
} else {
$instance->expects($this->never())->method('decryptAllUsersFiles');
}
$instance->decryptAll($this->inputInterface, $this->outputInterface, $user);
}
public function dataTrueFalse() {
return [
[true],
[false]
];
}
/**
* @dataProvider dataTrueFalse
*/
public function testPrepareEncryptionModules($success) {
$user = 'user1';
$dummyEncryptionModule = $this->getMockBuilder('OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()->getMock();
$dummyEncryptionModule->expects($this->once())
->method('prepareDecryptAll')
->with($this->inputInterface, $this->outputInterface, $user)
->willReturn($success);
$callback = function() use ($dummyEncryptionModule) {return $dummyEncryptionModule;};
$moduleDescription = [
'id' => 'id',
'displayName' => 'displayName',
'callback' => $callback
];
$this->encryptionManager->expects($this->once())
->method('getEncryptionModules')
->willReturn([$moduleDescription]);
$this->assertSame($success,
$this->invokePrivate($this->instance, 'prepareEncryptionModules', [$user])
);
}
/**
* @dataProvider dataTestDecryptAllUsersFiles
*/
public function testDecryptAllUsersFiles($user) {
/** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject | $instance */
$instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
->setConstructorArgs(
[
$this->encryptionManager,
$this->userManager,
$this->view
]
)
->setMethods(['decryptUsersFiles'])
->getMock();
$this->invokePrivate($instance, 'input', [$this->inputInterface]);
$this->invokePrivate($instance, 'output', [$this->outputInterface]);
if (empty($user)) {
$this->userManager->expects($this->once())
->method('getBackends')
->willReturn([$this->userInterface]);
$this->userInterface->expects($this->any())
->method('getUsers')
->willReturn(['user1', 'user2']);
$instance->expects($this->at(0))
->method('decryptUsersFiles')
->with('user1');
$instance->expects($this->at(1))
->method('decryptUsersFiles')
->with('user2');
} else {
$instance->expects($this->once())
->method('decryptUsersFiles')
->with($user);
}
$this->invokePrivate($instance, 'decryptAllUsersFiles', [$user]);
}
public function dataTestDecryptAllUsersFiles() {
return [
['user1'],
['']
];
}
public function testDecryptUsersFiles() {
/** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject $instance */
$instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
->setConstructorArgs(
[
$this->encryptionManager,
$this->userManager,
$this->view
]
)
->setMethods(['decryptFile'])
->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;
}
);
$instance->expects($this->at(0))
->method('decryptFile')
->with('/user1/files/bar');
$instance->expects($this->at(1))
->method('decryptFile')
->with('/user1/files/foo/subfile');
$progressBar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')
->disableOriginalConstructor()->getMock();
$this->invokePrivate($instance, 'decryptUsersFiles', ['user1', $progressBar, '']);
}
public function testDecryptFile() {
$path = 'test.txt';
/** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject $instance */
$instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
->setConstructorArgs(
[
$this->encryptionManager,
$this->userManager,
$this->view
]
)
->setMethods(['getTimestamp'])
->getMock();
$instance->expects($this->any())->method('getTimestamp')->willReturn(42);
$this->view->expects($this->once())
->method('copy')
->with($path, $path . '.decrypted.42');
$this->view->expects($this->once())
->method('rename')
->with($path . '.decrypted.42', $path);
$this->assertTrue(
$this->invokePrivate($instance, 'decryptFile', [$path])
);
}
public function testDecryptFileFailure() {
$path = 'test.txt';
/** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject $instance */
$instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
->setConstructorArgs(
[
$this->encryptionManager,
$this->userManager,
$this->view
]
)
->setMethods(['getTimestamp'])
->getMock();
$instance->expects($this->any())->method('getTimestamp')->willReturn(42);
$this->view->expects($this->once())
->method('copy')
->with($path, $path . '.decrypted.42')
->willReturnCallback(function() { throw new DecryptionFailedException();});
$this->view->expects($this->never())->method('rename');
$this->view->expects($this->once())
->method('file_exists')
->with($path . '.decrypted.42')
->willReturn(true);
$this->view->expects($this->once())
->method('unlink')
->with($path . '.decrypted.42');
$this->assertFalse(
$this->invokePrivate($instance, 'decryptFile', [$path])
);
}
}

View File

@ -194,7 +194,7 @@ class Encryption extends \Test\Files\Storage\Storage {
protected function buildMockModule() {
$this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()
->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll'])
->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll'])
->getMock();
$this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');

View File

@ -305,7 +305,7 @@ class Encryption 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'])
->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll'])
->getMock();
$encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');