parent
230029e509
commit
9bd4f2d41e
@ -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); |
||||
} |
||||
} |
@ -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'] |
||||
]; |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
||||
|
||||
} |
@ -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]); |
||||
} |
||||
|
||||
} |
@ -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]) |
||||
); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue