occ script to disable encryption and to decrypt all files again
This commit is contained in:
parent
230029e509
commit
9bd4f2d41e
|
@ -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');
|
||||
|
|
Loading…
Reference in New Issue