@@ -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() { |
@@ -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); | |||
} | |||
} |
@@ -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 |
@@ -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'); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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'] | |||
]; | |||
} | |||
} |
@@ -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'); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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())); |
@@ -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); | |||
} | |||
} |
@@ -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 = ''); | |||
} |
@@ -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]) | |||
); | |||
} | |||
} |
@@ -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'); |
@@ -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'); |