Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>tags/v14.0.0beta1
@@ -28,7 +28,7 @@ namespace OCA\Encryption\AppInfo; | |||
$encryptionSystemReady = \OC::$server->getEncryptionManager()->isReady(); | |||
$app = new Application([], $encryptionSystemReady); | |||
$app = new Application(); | |||
if ($encryptionSystemReady) { | |||
$app->registerEncryptionModule(); | |||
$app->registerHooks(); |
@@ -43,7 +43,6 @@ | |||
<commands> | |||
<command>OCA\Encryption\Command\EnableMasterKey</command> | |||
<command>OCA\Encryption\Command\DisableMasterKey</command> | |||
<command>OCA\Encryption\Command\MigrateKeys</command> | |||
</commands> | |||
<settings> |
@@ -9,7 +9,6 @@ return array( | |||
'OCA\\Encryption\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', | |||
'OCA\\Encryption\\Command\\DisableMasterKey' => $baseDir . '/../lib/Command/DisableMasterKey.php', | |||
'OCA\\Encryption\\Command\\EnableMasterKey' => $baseDir . '/../lib/Command/EnableMasterKey.php', | |||
'OCA\\Encryption\\Command\\MigrateKeys' => $baseDir . '/../lib/Command/MigrateKeys.php', | |||
'OCA\\Encryption\\Controller\\RecoveryController' => $baseDir . '/../lib/Controller/RecoveryController.php', | |||
'OCA\\Encryption\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php', | |||
'OCA\\Encryption\\Controller\\StatusController' => $baseDir . '/../lib/Controller/StatusController.php', | |||
@@ -25,7 +24,6 @@ return array( | |||
'OCA\\Encryption\\Hooks\\Contracts\\IHook' => $baseDir . '/../lib/Hooks/Contracts/IHook.php', | |||
'OCA\\Encryption\\Hooks\\UserHooks' => $baseDir . '/../lib/Hooks/UserHooks.php', | |||
'OCA\\Encryption\\KeyManager' => $baseDir . '/../lib/KeyManager.php', | |||
'OCA\\Encryption\\Migration' => $baseDir . '/../lib/Migration.php', | |||
'OCA\\Encryption\\Migration\\SetMasterKeyStatus' => $baseDir . '/../lib/Migration/SetMasterKeyStatus.php', | |||
'OCA\\Encryption\\Recovery' => $baseDir . '/../lib/Recovery.php', | |||
'OCA\\Encryption\\Session' => $baseDir . '/../lib/Session.php', |
@@ -24,7 +24,6 @@ class ComposerStaticInitEncryption | |||
'OCA\\Encryption\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', | |||
'OCA\\Encryption\\Command\\DisableMasterKey' => __DIR__ . '/..' . '/../lib/Command/DisableMasterKey.php', | |||
'OCA\\Encryption\\Command\\EnableMasterKey' => __DIR__ . '/..' . '/../lib/Command/EnableMasterKey.php', | |||
'OCA\\Encryption\\Command\\MigrateKeys' => __DIR__ . '/..' . '/../lib/Command/MigrateKeys.php', | |||
'OCA\\Encryption\\Controller\\RecoveryController' => __DIR__ . '/..' . '/../lib/Controller/RecoveryController.php', | |||
'OCA\\Encryption\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php', | |||
'OCA\\Encryption\\Controller\\StatusController' => __DIR__ . '/..' . '/../lib/Controller/StatusController.php', | |||
@@ -40,7 +39,6 @@ class ComposerStaticInitEncryption | |||
'OCA\\Encryption\\Hooks\\Contracts\\IHook' => __DIR__ . '/..' . '/../lib/Hooks/Contracts/IHook.php', | |||
'OCA\\Encryption\\Hooks\\UserHooks' => __DIR__ . '/..' . '/../lib/Hooks/UserHooks.php', | |||
'OCA\\Encryption\\KeyManager' => __DIR__ . '/..' . '/../lib/KeyManager.php', | |||
'OCA\\Encryption\\Migration' => __DIR__ . '/..' . '/../lib/Migration.php', | |||
'OCA\\Encryption\\Migration\\SetMasterKeyStatus' => __DIR__ . '/..' . '/../lib/Migration/SetMasterKeyStatus.php', | |||
'OCA\\Encryption\\Recovery' => __DIR__ . '/..' . '/../lib/Recovery.php', | |||
'OCA\\Encryption\\Session' => __DIR__ . '/..' . '/../lib/Session.php', |
@@ -55,19 +55,12 @@ class Application extends \OCP\AppFramework\App { | |||
/** | |||
* @param array $urlParams | |||
* @param bool $encryptionSystemReady | |||
*/ | |||
public function __construct($urlParams = array(), $encryptionSystemReady = true) { | |||
public function __construct($urlParams = array()) { | |||
parent::__construct('encryption', $urlParams); | |||
$this->encryptionManager = \OC::$server->getEncryptionManager(); | |||
$this->config = \OC::$server->getConfig(); | |||
$this->registerServices(); | |||
if($encryptionSystemReady === false) { | |||
/** @var Session $session */ | |||
$session = $this->getContainer()->query('Session'); | |||
$session->setStatus(Session::RUN_MIGRATION); | |||
} | |||
} | |||
public function setUp() { |
@@ -1,127 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Thomas Müller <thomas.mueller@tmit.eu> | |||
* | |||
* @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\Command; | |||
use OC\Files\View; | |||
use OCA\Encryption\Migration; | |||
use OCP\IConfig; | |||
use OCP\IDBConnection; | |||
use OCP\ILogger; | |||
use OCP\IUserBackend; | |||
use OCP\IUserManager; | |||
use Symfony\Component\Console\Command\Command; | |||
use Symfony\Component\Console\Input\InputArgument; | |||
use Symfony\Component\Console\Input\InputInterface; | |||
use Symfony\Component\Console\Output\OutputInterface; | |||
class MigrateKeys extends Command { | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var View */ | |||
private $view; | |||
/** @var IDBConnection */ | |||
private $connection; | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** | |||
* @param IUserManager $userManager | |||
* @param View $view | |||
* @param IDBConnection $connection | |||
* @param IConfig $config | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct(IUserManager $userManager, | |||
View $view, | |||
IDBConnection $connection, | |||
IConfig $config, | |||
ILogger $logger) { | |||
$this->userManager = $userManager; | |||
$this->view = $view; | |||
$this->connection = $connection; | |||
$this->config = $config; | |||
$this->logger = $logger; | |||
parent::__construct(); | |||
} | |||
protected function configure() { | |||
$this | |||
->setName('encryption:migrate') | |||
->setDescription('initial migration to encryption 2.0') | |||
->addArgument( | |||
'user_id', | |||
InputArgument::OPTIONAL | InputArgument::IS_ARRAY, | |||
'will migrate keys of the given user(s)' | |||
); | |||
} | |||
protected function execute(InputInterface $input, OutputInterface $output) { | |||
// perform system reorganization | |||
$migration = new Migration($this->config, $this->view, $this->connection, $this->logger); | |||
$users = $input->getArgument('user_id'); | |||
if (!empty($users)) { | |||
foreach ($users as $user) { | |||
if ($this->userManager->userExists($user)) { | |||
$output->writeln("Migrating keys <info>$user</info>"); | |||
$migration->reorganizeFolderStructureForUser($user); | |||
} else { | |||
$output->writeln("<error>Unknown user $user</error>"); | |||
} | |||
} | |||
} else { | |||
$output->writeln("Reorganize system folder structure"); | |||
$migration->reorganizeSystemFolderStructure(); | |||
$migration->updateDB(); | |||
foreach($this->userManager->getBackends() as $backend) { | |||
$name = get_class($backend); | |||
if ($backend instanceof IUserBackend) { | |||
$name = $backend->getBackendName(); | |||
} | |||
$output->writeln("Migrating keys for users on backend <info>$name</info>"); | |||
$limit = 500; | |||
$offset = 0; | |||
do { | |||
$users = $backend->getUsers('', $limit, $offset); | |||
foreach ($users as $user) { | |||
$output->writeln(" <info>$user</info>"); | |||
$migration->reorganizeFolderStructureForUser($user); | |||
} | |||
$offset += $limit; | |||
} while(count($users) >= $limit); | |||
} | |||
} | |||
$migration->finalCleanUp(); | |||
} | |||
} |
@@ -73,12 +73,6 @@ class StatusController extends Controller { | |||
$status = 'error'; | |||
$message = 'no valid init status'; | |||
switch( $this->session->getStatus()) { | |||
case Session::RUN_MIGRATION: | |||
$status = 'interactionNeeded'; | |||
$message = (string)$this->l->t( | |||
'You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please run \'occ encryption:migrate\' or contact your administrator' | |||
); | |||
break; | |||
case Session::INIT_EXECUTED: | |||
$status = 'interactionNeeded'; | |||
$message = (string)$this->l->t( |
@@ -1,395 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Joas Schilling <coding@schilljs.com> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* | |||
* @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; | |||
use OC\Files\View; | |||
use OCP\App\IAppManager; | |||
use OCP\IConfig; | |||
use OCP\IDBConnection; | |||
use OCP\ILogger; | |||
class Migration { | |||
private $moduleId; | |||
/** @var \OC\Files\View */ | |||
private $view; | |||
/** @var \OCP\IDBConnection */ | |||
private $connection; | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var string*/ | |||
protected $installedVersion; | |||
/** @var IAppManager */ | |||
protected $appManager; | |||
/** | |||
* @param IConfig $config | |||
* @param View $view | |||
* @param IDBConnection $connection | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct(IConfig $config, View $view, IDBConnection $connection, ILogger $logger, IAppManager $appManager) { | |||
$this->view = $view; | |||
$this->view->disableCacheUpdate(); | |||
$this->connection = $connection; | |||
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID; | |||
$this->config = $config; | |||
$this->logger = $logger; | |||
$this->installedVersion = $this->config->getAppValue('files_encryption', 'installed_version', '-1'); | |||
$this->appManager = $appManager; | |||
} | |||
public function finalCleanUp() { | |||
$this->view->deleteAll('files_encryption/public_keys'); | |||
$this->updateFileCache(); | |||
$this->config->deleteAppValue('files_encryption', 'installed_version'); | |||
} | |||
/** | |||
* update file cache, copy unencrypted_size to the 'size' column | |||
*/ | |||
private function updateFileCache() { | |||
// make sure that we don't update the file cache multiple times | |||
// only update during the first run | |||
if ($this->installedVersion !== '-1') { | |||
$query = $this->connection->getQueryBuilder(); | |||
$query->update('filecache') | |||
->set('size', 'unencrypted_size') | |||
->where($query->expr()->eq('encrypted', $query->createParameter('encrypted'))) | |||
->setParameter('encrypted', 1); | |||
$query->execute(); | |||
} | |||
} | |||
/** | |||
* iterate through users and reorganize the folder structure | |||
*/ | |||
public function reorganizeFolderStructure() { | |||
$this->reorganizeSystemFolderStructure(); | |||
$limit = 500; | |||
$offset = 0; | |||
do { | |||
$users = \OCP\User::getUsers('', $limit, $offset); | |||
foreach ($users as $user) { | |||
$this->reorganizeFolderStructureForUser($user); | |||
} | |||
$offset += $limit; | |||
} while (count($users) >= $limit); | |||
} | |||
/** | |||
* reorganize system wide folder structure | |||
*/ | |||
public function reorganizeSystemFolderStructure() { | |||
$this->createPathForKeys('/files_encryption'); | |||
// backup system wide folders | |||
$this->backupSystemWideKeys(); | |||
// rename system wide mount point | |||
$this->renameFileKeys('', '/files_encryption/keys'); | |||
// rename system private keys | |||
$this->renameSystemPrivateKeys(); | |||
$storage = $this->view->getMount('')->getStorage(); | |||
$storage->getScanner()->scan('files_encryption'); | |||
} | |||
/** | |||
* reorganize folder structure for user | |||
* | |||
* @param string $user | |||
*/ | |||
public function reorganizeFolderStructureForUser($user) { | |||
// backup all keys | |||
\OC_Util::tearDownFS(); | |||
\OC_Util::setupFS($user); | |||
if ($this->backupUserKeys($user)) { | |||
// rename users private key | |||
$this->renameUsersPrivateKey($user); | |||
$this->renameUsersPublicKey($user); | |||
// rename file keys | |||
$path = '/files_encryption/keys'; | |||
$this->renameFileKeys($user, $path); | |||
$trashPath = '/files_trashbin/keys'; | |||
if ($this->appManager->isEnabledForUser('files_trashbin') && $this->view->is_dir($user . '/' . $trashPath)) { | |||
$this->renameFileKeys($user, $trashPath, true); | |||
$this->view->deleteAll($trashPath); | |||
} | |||
// delete old folders | |||
$this->deleteOldKeys($user); | |||
$this->view->getMount('/' . $user)->getStorage()->getScanner()->scan('files_encryption'); | |||
} | |||
} | |||
/** | |||
* update database | |||
*/ | |||
public function updateDB() { | |||
// make sure that we don't update the file cache multiple times | |||
// only update during the first run | |||
if ($this->installedVersion === '-1') { | |||
return; | |||
} | |||
// delete left-over from old encryption which is no longer needed | |||
$this->config->deleteAppValue('files_encryption', 'ocsid'); | |||
$this->config->deleteAppValue('files_encryption', 'types'); | |||
$this->config->deleteAppValue('files_encryption', 'enabled'); | |||
$oldAppValues = $this->connection->getQueryBuilder(); | |||
$oldAppValues->select('*') | |||
->from('appconfig') | |||
->where($oldAppValues->expr()->eq('appid', $oldAppValues->createParameter('appid'))) | |||
->setParameter('appid', 'files_encryption'); | |||
$appSettings = $oldAppValues->execute(); | |||
while ($row = $appSettings->fetch()) { | |||
// 'installed_version' gets deleted at the end of the migration process | |||
if ($row['configkey'] !== 'installed_version' ) { | |||
$this->config->setAppValue('encryption', $row['configkey'], $row['configvalue']); | |||
$this->config->deleteAppValue('files_encryption', $row['configkey']); | |||
} | |||
} | |||
$oldPreferences = $this->connection->getQueryBuilder(); | |||
$oldPreferences->select('*') | |||
->from('preferences') | |||
->where($oldPreferences->expr()->eq('appid', $oldPreferences->createParameter('appid'))) | |||
->setParameter('appid', 'files_encryption'); | |||
$preferenceSettings = $oldPreferences->execute(); | |||
while ($row = $preferenceSettings->fetch()) { | |||
$this->config->setUserValue($row['userid'], 'encryption', $row['configkey'], $row['configvalue']); | |||
$this->config->deleteUserValue($row['userid'], 'files_encryption', $row['configkey']); | |||
} | |||
} | |||
/** | |||
* create backup of system-wide keys | |||
*/ | |||
private function backupSystemWideKeys() { | |||
$backupDir = 'encryption_migration_backup_' . date("Y-m-d_H-i-s"); | |||
$this->view->mkdir($backupDir); | |||
$this->view->copy('files_encryption', $backupDir . '/files_encryption'); | |||
} | |||
/** | |||
* create backup of user specific keys | |||
* | |||
* @param string $user | |||
* @return bool | |||
*/ | |||
private function backupUserKeys($user) { | |||
$encryptionDir = $user . '/files_encryption'; | |||
if ($this->view->is_dir($encryptionDir)) { | |||
$backupDir = $user . '/encryption_migration_backup_' . date("Y-m-d_H-i-s"); | |||
$this->view->mkdir($backupDir); | |||
$this->view->copy($encryptionDir, $backupDir); | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* rename system-wide private keys | |||
*/ | |||
private function renameSystemPrivateKeys() { | |||
$dh = $this->view->opendir('files_encryption'); | |||
$this->createPathForKeys('/files_encryption/' . $this->moduleId ); | |||
if (is_resource($dh)) { | |||
while (($privateKey = readdir($dh)) !== false) { | |||
if (!\OC\Files\Filesystem::isIgnoredDir($privateKey) ) { | |||
if (!$this->view->is_dir('/files_encryption/' . $privateKey)) { | |||
$this->view->rename('files_encryption/' . $privateKey, 'files_encryption/' . $this->moduleId . '/' . $privateKey); | |||
$this->renameSystemPublicKey($privateKey); | |||
} | |||
} | |||
} | |||
closedir($dh); | |||
} | |||
} | |||
/** | |||
* rename system wide public key | |||
* | |||
* @param string $privateKey private key for which we want to rename the corresponding public key | |||
*/ | |||
private function renameSystemPublicKey($privateKey) { | |||
$publicKey = substr($privateKey,0 , strrpos($privateKey, '.privateKey')) . '.publicKey'; | |||
$this->view->rename('files_encryption/public_keys/' . $publicKey, 'files_encryption/' . $this->moduleId . '/' . $publicKey); | |||
} | |||
/** | |||
* rename user-specific private keys | |||
* | |||
* @param string $user | |||
*/ | |||
private function renameUsersPrivateKey($user) { | |||
$oldPrivateKey = $user . '/files_encryption/' . $user . '.privateKey'; | |||
$newPrivateKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.privateKey'; | |||
if ($this->view->file_exists($oldPrivateKey)) { | |||
$this->createPathForKeys(dirname($newPrivateKey)); | |||
$this->view->rename($oldPrivateKey, $newPrivateKey); | |||
} | |||
} | |||
/** | |||
* rename user-specific public keys | |||
* | |||
* @param string $user | |||
*/ | |||
private function renameUsersPublicKey($user) { | |||
$oldPublicKey = '/files_encryption/public_keys/' . $user . '.publicKey'; | |||
$newPublicKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.publicKey'; | |||
if ($this->view->file_exists($oldPublicKey)) { | |||
$this->createPathForKeys(dirname($newPublicKey)); | |||
$this->view->rename($oldPublicKey, $newPublicKey); | |||
} | |||
} | |||
/** | |||
* rename file keys | |||
* | |||
* @param string $user | |||
* @param string $path | |||
* @param bool $trash | |||
*/ | |||
private function renameFileKeys($user, $path, $trash = false) { | |||
if ($this->view->is_dir($user . '/' . $path) === false) { | |||
$this->logger->info('Skip dir /' . $user . '/' . $path . ': does not exist'); | |||
return; | |||
} | |||
$dh = $this->view->opendir($user . '/' . $path); | |||
if (is_resource($dh)) { | |||
while (($file = readdir($dh)) !== false) { | |||
if (!\OC\Files\Filesystem::isIgnoredDir($file)) { | |||
if ($this->view->is_dir($user . '/' . $path . '/' . $file)) { | |||
$this->renameFileKeys($user, $path . '/' . $file, $trash); | |||
} else { | |||
$target = $this->getTargetDir($user, $path, $file, $trash); | |||
if ($target !== false) { | |||
$this->createPathForKeys(dirname($target)); | |||
$this->view->rename($user . '/' . $path . '/' . $file, $target); | |||
} else { | |||
$this->logger->warning( | |||
'did not move key "' . $file | |||
. '" could not find the corresponding file in /data/' . $user . '/files.' | |||
. 'Most likely the key was already moved in a previous migration run and is already on the right place.'); | |||
} | |||
} | |||
} | |||
} | |||
closedir($dh); | |||
} | |||
} | |||
/** | |||
* get system mount points | |||
* wrap static method so that it can be mocked for testing | |||
* | |||
* @internal | |||
* @return array | |||
*/ | |||
protected function getSystemMountPoints() { | |||
return \OC_Mount_Config::getSystemMountPoints(); | |||
} | |||
/** | |||
* generate target directory | |||
* | |||
* @param string $user | |||
* @param string $keyPath | |||
* @param string $filename | |||
* @param bool $trash | |||
* @return string | |||
*/ | |||
private function getTargetDir($user, $keyPath, $filename, $trash) { | |||
if ($trash) { | |||
$filePath = substr($keyPath, strlen('/files_trashbin/keys/')); | |||
$targetDir = $user . '/files_encryption/keys/files_trashbin/' . $filePath . '/' . $this->moduleId . '/' . $filename; | |||
} else { | |||
$filePath = substr($keyPath, strlen('/files_encryption/keys/')); | |||
$targetDir = $user . '/files_encryption/keys/files/' . $filePath . '/' . $this->moduleId . '/' . $filename; | |||
} | |||
if ($user === '') { | |||
// for system wide mounts we need to check if the mount point really exists | |||
$normalized = \OC\Files\Filesystem::normalizePath($filePath); | |||
$systemMountPoints = $this->getSystemMountPoints(); | |||
foreach ($systemMountPoints as $mountPoint) { | |||
$normalizedMountPoint = \OC\Files\Filesystem::normalizePath($mountPoint['mountpoint']) . '/'; | |||
if (strpos($normalized, $normalizedMountPoint) === 0) | |||
return $targetDir; | |||
} | |||
} else if ($trash === false && $this->view->file_exists('/' . $user. '/files/' . $filePath)) { | |||
return $targetDir; | |||
} else if ($trash === true && $this->view->file_exists('/' . $user. '/files_trashbin/' . $filePath)) { | |||
return $targetDir; | |||
} | |||
return false; | |||
} | |||
/** | |||
* delete old keys | |||
* | |||
* @param string $user | |||
*/ | |||
private function deleteOldKeys($user) { | |||
$this->view->deleteAll($user . '/files_encryption/keyfiles'); | |||
$this->view->deleteAll($user . '/files_encryption/share-keys'); | |||
} | |||
/** | |||
* create directories for the keys recursively | |||
* | |||
* @param string $path | |||
*/ | |||
private function createPathForKeys($path) { | |||
if (!$this->view->file_exists($path)) { | |||
$sub_dirs = explode('/', $path); | |||
$dir = ''; | |||
foreach ($sub_dirs as $sub_dir) { | |||
$dir .= '/' . $sub_dir; | |||
if (!$this->view->is_dir($dir)) { | |||
$this->view->mkdir($dir); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -36,7 +36,6 @@ class Session { | |||
const NOT_INITIALIZED = '0'; | |||
const INIT_EXECUTED = '1'; | |||
const INIT_SUCCESSFUL = '2'; | |||
const RUN_MIGRATION = '3'; | |||
/** | |||
* @param ISession $session |
@@ -92,12 +92,11 @@ class StatusControllerTest extends TestCase { | |||
} | |||
public function dataTestGetStatus() { | |||
return array( | |||
array(Session::RUN_MIGRATION, 'interactionNeeded'), | |||
array(Session::INIT_EXECUTED, 'interactionNeeded'), | |||
array(Session::INIT_SUCCESSFUL, 'success'), | |||
array(Session::NOT_INITIALIZED, 'interactionNeeded'), | |||
array('unknown', 'error'), | |||
); | |||
return [ | |||
[Session::INIT_EXECUTED, 'interactionNeeded'], | |||
[Session::INIT_SUCCESSFUL, 'success'], | |||
[Session::NOT_INITIALIZED, 'interactionNeeded'], | |||
['unknown', 'error'], | |||
]; | |||
} | |||
} |
@@ -1,597 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Joas Schilling <coding@schilljs.com> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @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; | |||
use OC\Files\View; | |||
use OCA\Encryption\Migration; | |||
use OCP\ILogger; | |||
/** | |||
* Class MigrationTest | |||
* | |||
* @package OCA\Encryption\Tests | |||
* @group DB | |||
*/ | |||
class MigrationTest extends \Test\TestCase { | |||
const TEST_ENCRYPTION_MIGRATION_USER1='test_encryption_user1'; | |||
const TEST_ENCRYPTION_MIGRATION_USER2='test_encryption_user2'; | |||
const TEST_ENCRYPTION_MIGRATION_USER3='test_encryption_user3'; | |||
/** @var \OC\Files\View */ | |||
private $view; | |||
private $public_share_key_id = 'share_key_id'; | |||
private $recovery_key_id = 'recovery_key_id'; | |||
private $moduleId; | |||
/** @var \PHPUnit_Framework_MockObject_MockObject|ILogger */ | |||
private $logger; | |||
public static function setUpBeforeClass() { | |||
parent::setUpBeforeClass(); | |||
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo'); | |||
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo'); | |||
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo'); | |||
} | |||
public static function tearDownAfterClass() { | |||
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
if ($user !== null) { $user->delete(); } | |||
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
if ($user !== null) { $user->delete(); } | |||
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
if ($user !== null) { $user->delete(); } | |||
parent::tearDownAfterClass(); | |||
} | |||
public function setUp() { | |||
$this->logger = $this->getMockBuilder(ILogger::class)->disableOriginalConstructor()->getMock(); | |||
$this->view = new \OC\Files\View(); | |||
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID; | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function createDummyShareKeys($uid) { | |||
$this->loginAsUser($uid); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder2/file.2.1'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey' , 'data'); | |||
if ($this->public_share_key_id) { | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . $this->public_share_key_id . '.shareKey' , 'data'); | |||
} | |||
if ($this->recovery_key_id) { | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/' . $this->recovery_key_id . '.shareKey' , 'data'); | |||
} | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function createDummyUserKeys($uid) { | |||
$this->loginAsUser($uid); | |||
$this->view->mkdir($uid . '/files_encryption/'); | |||
$this->view->mkdir('/files_encryption/public_keys'); | |||
$this->view->file_put_contents($uid . '/files_encryption/' . $uid . '.privateKey', 'privateKey'); | |||
$this->view->file_put_contents('/files_encryption/public_keys/' . $uid . '.publicKey', 'publicKey'); | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function createDummyFileKeys($uid) { | |||
$this->loginAsUser($uid); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1'); | |||
$this->view->mkdir($uid . '/files_encryption/keys/folder2/file.2.1'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/folder3/file3/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/folder2/file2/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder1/file.1/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/fileKey' , 'data'); | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function createDummyFiles($uid) { | |||
$this->loginAsUser($uid); | |||
$this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3'); | |||
$this->view->mkdir($uid . '/files/folder1/folder2/file2'); | |||
$this->view->mkdir($uid . '/files/folder1/file.1'); | |||
$this->view->mkdir($uid . '/files/folder2/file.2.1'); | |||
$this->view->file_put_contents($uid . '/files/folder1/folder2/folder3/file3/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files/folder1/folder2/file2/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files/folder1/file.1/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files/folder2/file.2.1/fileKey' , 'data'); | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function createDummyFilesInTrash($uid) { | |||
$this->loginAsUser($uid); | |||
$this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864'); | |||
$this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/fileKey' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/fileKey' , 'data'); | |||
// create the files itself | |||
$this->view->mkdir($uid . '/files_trashbin/folder1.d7437648723'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/file1.d5457864' , 'data'); | |||
$this->view->file_put_contents($uid . '/files_trashbin/folder1.d7437648723/file2' , 'data'); | |||
} | |||
protected function createDummySystemWideKeys() { | |||
$this->view->mkdir('files_encryption'); | |||
$this->view->mkdir('files_encryption/public_keys'); | |||
$this->view->file_put_contents('files_encryption/systemwide_1.privateKey', 'data'); | |||
$this->view->file_put_contents('files_encryption/systemwide_2.privateKey', 'data'); | |||
$this->view->file_put_contents('files_encryption/public_keys/systemwide_1.publicKey', 'data'); | |||
$this->view->file_put_contents('files_encryption/public_keys/systemwide_2.publicKey', 'data'); | |||
} | |||
public function testMigrateToNewFolderStructure() { | |||
$this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->createDummyUserKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
$this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->createDummyShareKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
$this->createDummyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
// no user for system wide mount points | |||
$this->createDummyFileKeys(''); | |||
$this->createDummyShareKeys(''); | |||
$this->createDummySystemWideKeys(); | |||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Encryption\Migration $m */ | |||
$m = $this->getMockBuilder(Migration::class) | |||
->setConstructorArgs( | |||
[ | |||
\OC::$server->getConfig(), | |||
new \OC\Files\View(), | |||
\OC::$server->getDatabaseConnection(), | |||
$this->logger, | |||
\OC::$server->getAppManager() | |||
] | |||
)->setMethods(['getSystemMountPoints'])->getMock(); | |||
$m->expects($this->any())->method('getSystemMountPoints') | |||
->will($this->returnValue([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']])); | |||
$m->reorganizeFolderStructure(); | |||
// even if it runs twice folder should always move only once | |||
$m->reorganizeFolderStructure(); | |||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->assertTrue( | |||
$this->view->file_exists( | |||
self::TEST_ENCRYPTION_MIGRATION_USER1 . '/files_encryption/' . | |||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.publicKey') | |||
); | |||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->assertTrue( | |||
$this->view->file_exists( | |||
self::TEST_ENCRYPTION_MIGRATION_USER2 . '/files_encryption/' . | |||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.publicKey') | |||
); | |||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
$this->assertTrue( | |||
$this->view->file_exists( | |||
self::TEST_ENCRYPTION_MIGRATION_USER3 . '/files_encryption/' . | |||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.publicKey') | |||
); | |||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->assertTrue( | |||
$this->view->file_exists( | |||
'/files_encryption/' . $this->moduleId . '/systemwide_1.publicKey') | |||
); | |||
$this->assertTrue( | |||
$this->view->file_exists( | |||
'/files_encryption/' . $this->moduleId . '/systemwide_2.publicKey') | |||
); | |||
$this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
$this->verifyNewKeyPath(self::TEST_ENCRYPTION_MIGRATION_USER3); | |||
// system wide keys | |||
$this->verifyNewKeyPath(''); | |||
// trash | |||
$this->verifyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2); | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function verifyFilesInTrash($uid) { | |||
$this->loginAsUser($uid); | |||
// share keys | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') | |||
); | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') | |||
); | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/folder1.d7437648723/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey') | |||
); | |||
// file keys | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/fileKey') | |||
); | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/fileKey') | |||
); | |||
$this->assertTrue( | |||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/folder1.d7437648723/file2/' . $this->moduleId . '/fileKey') | |||
); | |||
} | |||
/** | |||
* @param string $uid | |||
*/ | |||
protected function verifyNewKeyPath($uid) { | |||
// private key | |||
if ($uid !== '') { | |||
$this->loginAsUser($uid); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/' . $this->moduleId . '/'. $uid . '.privateKey')); | |||
} | |||
// file keys | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/fileKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/fileKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/fileKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' .$this->moduleId . '/fileKey')); | |||
// share keys | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/folder3/file3/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/folder2/file2/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder1/file.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.shareKey')); | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.shareKey')); | |||
if ($this->public_share_key_id) { | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . $this->public_share_key_id . '.shareKey')); | |||
} | |||
if ($this->recovery_key_id) { | |||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/keys/files/folder2/file.2.1/' . $this->moduleId . '/' . $this->recovery_key_id . '.shareKey')); | |||
} | |||
} | |||
private function prepareDB() { | |||
$config = \OC::$server->getConfig(); | |||
$config->setAppValue('files_encryption', 'recoveryKeyId', 'recovery_id'); | |||
$config->setAppValue('files_encryption', 'publicShareKeyId', 'share_id'); | |||
$config->setAppValue('files_encryption', 'recoveryAdminEnabled', '1'); | |||
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'files_encryption', 'recoverKeyEnabled', '1'); | |||
//$this->invokePrivate($config, 'cache', [[]]); | |||
$cache = $this->invokePrivate(\OC::$server->getAppConfig(), 'cache'); | |||
unset($cache['encryption']); | |||
unset($cache['files_encryption']); | |||
$this->invokePrivate(\OC::$server->getAppConfig(), 'cache', [$cache]); | |||
$cache = $this->invokePrivate($config, 'userCache'); | |||
unset($cache[self::TEST_ENCRYPTION_MIGRATION_USER1]); | |||
$this->invokePrivate(\OC::$server->getAppConfig(), 'userCache', [$cache]); | |||
// delete default values set by the encryption app during initialization | |||
/** @var \OCP\IDBConnection $connection */ | |||
$connection = \OC::$server->getDatabaseConnection(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->delete('appconfig') | |||
->where($query->expr()->eq('appid', $query->createParameter('appid'))) | |||
->setParameter('appid', 'encryption'); | |||
$query->execute(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->delete('preferences') | |||
->where($query->expr()->eq('appid', $query->createParameter('appid'))) | |||
->setParameter('appid', 'encryption'); | |||
$query->execute(); | |||
} | |||
public function testUpdateDB() { | |||
$this->prepareDB(); | |||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger, \OC::$server->getAppManager()); | |||
$this->invokePrivate($m, 'installedVersion', ['0.7']); | |||
$m->updateDB(); | |||
$this->verifyDB('appconfig', 'files_encryption', 0); | |||
$this->verifyDB('preferences', 'files_encryption', 0); | |||
$this->verifyDB('appconfig', 'encryption', 3); | |||
$this->verifyDB('preferences', 'encryption', 1); | |||
} | |||
/** | |||
* test update db if the db already contain some existing new values | |||
*/ | |||
public function testUpdateDBExistingNewConfig() { | |||
$this->prepareDB(); | |||
$config = \OC::$server->getConfig(); | |||
$config->setAppValue('encryption', 'publicShareKeyId', 'wrong_share_id'); | |||
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9'); | |||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger, \OC::$server->getAppManager()); | |||
$this->invokePrivate($m, 'installedVersion', ['0.7']); | |||
$m->updateDB(); | |||
$this->verifyDB('appconfig', 'files_encryption', 0); | |||
$this->verifyDB('preferences', 'files_encryption', 0); | |||
$this->verifyDB('appconfig', 'encryption', 3); | |||
$this->verifyDB('preferences', 'encryption', 1); | |||
// check if the existing values where overwritten correctly | |||
/** @var \OC\DB\Connection $connection */ | |||
$connection = \OC::$server->getDatabaseConnection(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->select('configvalue') | |||
->from('appconfig') | |||
->where($query->expr()->andX( | |||
$query->expr()->eq('appid', $query->createParameter('appid')), | |||
$query->expr()->eq('configkey', $query->createParameter('configkey')) | |||
)) | |||
->setParameter('appid', 'encryption') | |||
->setParameter('configkey', 'publicShareKeyId'); | |||
$result = $query->execute(); | |||
$value = $result->fetch(); | |||
$this->assertTrue(isset($value['configvalue'])); | |||
$this->assertSame('share_id', $value['configvalue']); | |||
$query = $connection->getQueryBuilder(); | |||
$query->select('configvalue') | |||
->from('preferences') | |||
->where($query->expr()->andX( | |||
$query->expr()->eq('appid', $query->createParameter('appid')), | |||
$query->expr()->eq('configkey', $query->createParameter('configkey')), | |||
$query->expr()->eq('userid', $query->createParameter('userid')) | |||
)) | |||
->setParameter('appid', 'encryption') | |||
->setParameter('configkey', 'recoverKeyEnabled') | |||
->setParameter('userid', self::TEST_ENCRYPTION_MIGRATION_USER1); | |||
$result = $query->execute(); | |||
$value = $result->fetch(); | |||
$this->assertTrue(isset($value['configvalue'])); | |||
$this->assertSame('1', $value['configvalue']); | |||
} | |||
/** | |||
* @param string $table | |||
* @param string $appid | |||
* @param integer $expected | |||
*/ | |||
public function verifyDB($table, $appid, $expected) { | |||
/** @var \OCP\IDBConnection $connection */ | |||
$connection = \OC::$server->getDatabaseConnection(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->select('appid') | |||
->from($table) | |||
->where($query->expr()->eq('appid', $query->createParameter('appid'))) | |||
->setParameter('appid', $appid); | |||
$result = $query->execute(); | |||
$values = $result->fetchAll(); | |||
$this->assertSame($expected, | |||
count($values) | |||
); | |||
} | |||
/** | |||
* test update of the file cache | |||
*/ | |||
public function testUpdateFileCache() { | |||
$this->prepareFileCache(); | |||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger, \OC::$server->getAppManager()); | |||
$this->invokePrivate($m, 'installedVersion', ['0.7']); | |||
self::invokePrivate($m, 'updateFileCache'); | |||
// check results | |||
/** @var \OCP\IDBConnection $connection */ | |||
$connection = \OC::$server->getDatabaseConnection(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->select('*') | |||
->from('filecache'); | |||
$result = $query->execute(); | |||
$entries = $result->fetchAll(); | |||
foreach($entries as $entry) { | |||
if ((int)$entry['encrypted'] === 1) { | |||
$this->assertSame((int)$entry['unencrypted_size'], (int)$entry['size']); | |||
} else { | |||
$this->assertSame((int)$entry['unencrypted_size'] - 2, (int)$entry['size']); | |||
} | |||
} | |||
} | |||
public function prepareFileCache() { | |||
/** @var \OCP\IDBConnection $connection */ | |||
$connection = \OC::$server->getDatabaseConnection(); | |||
$query = $connection->getQueryBuilder(); | |||
$query->delete('filecache'); | |||
$query->execute(); | |||
$query = $connection->getQueryBuilder(); | |||
$result = $query->select('fileid') | |||
->from('filecache') | |||
->setMaxResults(1)->execute()->fetchAll(); | |||
$this->assertEmpty($result); | |||
$query = $connection->getQueryBuilder(); | |||
$query->insert('filecache') | |||
->values( | |||
array( | |||
'storage' => $query->createParameter('storage'), | |||
'path_hash' => $query->createParameter('path_hash'), | |||
'encrypted' => $query->createParameter('encrypted'), | |||
'size' => $query->createParameter('size'), | |||
'unencrypted_size' => $query->createParameter('unencrypted_size'), | |||
) | |||
); | |||
for ($i = 1; $i < 20; $i++) { | |||
$query->setParameter('storage', 1) | |||
->setParameter('path_hash', $i) | |||
->setParameter('encrypted', $i % 2) | |||
->setParameter('size', $i) | |||
->setParameter('unencrypted_size', $i + 2); | |||
$this->assertSame(1, | |||
$query->execute() | |||
); | |||
} | |||
$query = $connection->getQueryBuilder(); | |||
$result = $query->select('fileid') | |||
->from('filecache') | |||
->execute()->fetchAll(); | |||
$this->assertSame(19, count($result)); | |||
} | |||
/** | |||
* @dataProvider dataTestGetTargetDir | |||
*/ | |||
public function testGetTargetDir($user, $keyPath, $filename, $trash, $systemMounts, $expected) { | |||
$view = $this->getMockBuilder(View::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$view->expects($this->any())->method('file_exists')->willReturn(true); | |||
$m = $this->getMockBuilder(Migration::class) | |||
->setConstructorArgs( | |||
[ | |||
\OC::$server->getConfig(), | |||
$view, | |||
\OC::$server->getDatabaseConnection(), | |||
$this->logger, | |||
\OC::$server->getAppManager() | |||
] | |||
)->setMethods(['getSystemMountPoints'])->getMock(); | |||
$m->expects($this->any())->method('getSystemMountPoints') | |||
->willReturn($systemMounts); | |||
$this->assertSame($expected, | |||
$this->invokePrivate($m, 'getTargetDir', [$user, $keyPath, $filename, $trash]) | |||
); | |||
} | |||
public function dataTestGetTargetDir() { | |||
return [ | |||
[ | |||
'user1', | |||
'/files_encryption/keys/foo/bar.txt', | |||
'user1.shareKey', | |||
false, | |||
[], | |||
'user1/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' | |||
], | |||
[ | |||
'user1', | |||
'/files_trashbin/keys/foo/bar.txt', | |||
'user1.shareKey', | |||
true, | |||
[], | |||
'user1/files_encryption/keys/files_trashbin/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' | |||
], | |||
[ | |||
'', | |||
'/files_encryption/keys/foo/bar.txt', | |||
'user1.shareKey', | |||
false, | |||
[['mountpoint' => 'foo']], | |||
'/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey' | |||
], | |||
[ | |||
'', | |||
'/files_encryption/keys/foo/bar.txt', | |||
'user1.shareKey', | |||
false, | |||
[['mountpoint' => 'foobar']], | |||
false | |||
], | |||
[ | |||
'', | |||
'/files_encryption/keys/foobar/bar.txt', | |||
'user1.shareKey', | |||
false, | |||
[['mountpoint' => 'foo']], | |||
false | |||
] | |||
]; | |||
} | |||
} |
@@ -883,7 +883,6 @@ return array( | |||
'OC\\Settings\\Controller\\ChangePasswordController' => $baseDir . '/settings/Controller/ChangePasswordController.php', | |||
'OC\\Settings\\Controller\\CheckSetupController' => $baseDir . '/settings/Controller/CheckSetupController.php', | |||
'OC\\Settings\\Controller\\CommonSettingsTrait' => $baseDir . '/settings/Controller/CommonSettingsTrait.php', | |||
'OC\\Settings\\Controller\\EncryptionController' => $baseDir . '/settings/Controller/EncryptionController.php', | |||
'OC\\Settings\\Controller\\GroupsController' => $baseDir . '/settings/Controller/GroupsController.php', | |||
'OC\\Settings\\Controller\\LogSettingsController' => $baseDir . '/settings/Controller/LogSettingsController.php', | |||
'OC\\Settings\\Controller\\MailSettingsController' => $baseDir . '/settings/Controller/MailSettingsController.php', |
@@ -913,7 +913,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Settings\\Controller\\ChangePasswordController' => __DIR__ . '/../../..' . '/settings/Controller/ChangePasswordController.php', | |||
'OC\\Settings\\Controller\\CheckSetupController' => __DIR__ . '/../../..' . '/settings/Controller/CheckSetupController.php', | |||
'OC\\Settings\\Controller\\CommonSettingsTrait' => __DIR__ . '/../../..' . '/settings/Controller/CommonSettingsTrait.php', | |||
'OC\\Settings\\Controller\\EncryptionController' => __DIR__ . '/../../..' . '/settings/Controller/EncryptionController.php', | |||
'OC\\Settings\\Controller\\GroupsController' => __DIR__ . '/../../..' . '/settings/Controller/GroupsController.php', | |||
'OC\\Settings\\Controller\\LogSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/LogSettingsController.php', | |||
'OC\\Settings\\Controller\\MailSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/MailSettingsController.php', |
@@ -101,15 +101,6 @@ class Manager implements IManager { | |||
* @throws ServiceUnavailableException | |||
*/ | |||
public function isReady() { | |||
// check if we are still in transit between the old and the new encryption | |||
$oldEncryption = $this->config->getAppValue('files_encryption', 'installed_version'); | |||
if (!empty($oldEncryption)) { | |||
$warning = 'Installation is in transit between the old Encryption (ownCloud <= 8.0) | |||
and the new encryption. Please enable the "Default encryption module" | |||
and run \'occ encryption:migrate\''; | |||
$this->logger->warning($warning); | |||
return false; | |||
} | |||
if ($this->isKeyStorageReady() === false) { | |||
throw new ServiceUnavailableException('Key Storage is not ready'); |
@@ -1,161 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Joas Schilling <coding@schilljs.com> | |||
* @author Ko- <k.stoffelen@cs.ru.nl> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* | |||
* @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\Settings\Controller; | |||
use OC\Files\View; | |||
use OCA\Encryption\Migration; | |||
use OCP\App\IAppManager; | |||
use OCP\IDBConnection; | |||
use OCP\IL10N; | |||
use OCP\AppFramework\Controller; | |||
use OCP\ILogger; | |||
use OCP\IRequest; | |||
use OCP\IConfig; | |||
use OCP\IUserManager; | |||
/** | |||
* @package OC\Settings\Controller | |||
*/ | |||
class EncryptionController extends Controller { | |||
/** @var IL10N */ | |||
private $l10n; | |||
/** @var IDBConnection */ | |||
private $connection; | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var View */ | |||
private $view; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var IAppManager */ | |||
private $appManager; | |||
/** | |||
* @param string $appName | |||
* @param IRequest $request | |||
* @param IL10N $l10n | |||
* @param IConfig $config | |||
* @param IDBConnection $connection | |||
* @param IUserManager $userManager | |||
* @param View $view | |||
* @param ILogger $logger | |||
* @param IAppManager $appManager | |||
*/ | |||
public function __construct($appName, | |||
IRequest $request, | |||
IL10N $l10n, | |||
IConfig $config, | |||
IDBConnection $connection, | |||
IUserManager $userManager, | |||
View $view, | |||
ILogger $logger, | |||
IAppManager $appManager) { | |||
parent::__construct($appName, $request); | |||
$this->l10n = $l10n; | |||
$this->config = $config; | |||
$this->connection = $connection; | |||
$this->view = $view; | |||
$this->userManager = $userManager; | |||
$this->logger = $logger; | |||
$this->appManager = $appManager; | |||
} | |||
/** | |||
* @param IConfig $config | |||
* @param View $view | |||
* @param IDBConnection $connection | |||
* @param ILogger $logger | |||
* @param IAppManager $appManager | |||
* @return Migration | |||
*/ | |||
protected function getMigration(IConfig $config, | |||
View $view, | |||
IDBConnection $connection, | |||
ILogger $logger, | |||
IAppManager $appManager) { | |||
return new Migration($config, $view, $connection, $logger, $appManager); | |||
} | |||
/** | |||
* start migration | |||
* | |||
* @return array | |||
*/ | |||
public function startMigration() { | |||
// allow as long execution on the web server as possible | |||
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { | |||
@set_time_limit(0); | |||
} | |||
try { | |||
$migration = $this->getMigration($this->config, $this->view, $this->connection, $this->logger, $this->appManager); | |||
$migration->reorganizeSystemFolderStructure(); | |||
$migration->updateDB(); | |||
foreach ($this->userManager->getBackends() as $backend) { | |||
$limit = 500; | |||
$offset = 0; | |||
do { | |||
$users = $backend->getUsers('', $limit, $offset); | |||
foreach ($users as $user) { | |||
$migration->reorganizeFolderStructureForUser($user); | |||
} | |||
$offset += $limit; | |||
} while (count($users) >= $limit); | |||
} | |||
$migration->finalCleanUp(); | |||
} catch (\Exception $e) { | |||
return [ | |||
'data' => [ | |||
'message' => (string)$this->l10n->t('A problem occurred, please check your log files (Error: %s)', [$e->getMessage()]), | |||
], | |||
'status' => 'error', | |||
]; | |||
} | |||
return [ | |||
'data' => [ | |||
'message' => (string) $this->l10n->t('Migration Completed'), | |||
], | |||
'status' => 'success', | |||
]; | |||
} | |||
} |
@@ -30,7 +30,6 @@ use OC\Settings\Controller\AppSettingsController; | |||
use OC\Settings\Controller\AuthSettingsController; | |||
use OC\Settings\Controller\CertificateController; | |||
use OC\Settings\Controller\CheckSetupController; | |||
use OC\Settings\Controller\EncryptionController; | |||
use OC\Settings\Controller\GroupsController; | |||
use OC\Settings\Controller\LogSettingsController; | |||
use OC\Settings\Controller\MailSettingsController; | |||
@@ -74,7 +73,6 @@ class ApplicationTest extends TestCase { | |||
[AuthSettingsController::class, Controller::class], | |||
// Needs session: [CertificateController::class, Controller::class], | |||
[CheckSetupController::class, Controller::class], | |||
[EncryptionController::class, Controller::class], | |||
[GroupsController::class, Controller::class], | |||
[LogSettingsController::class, Controller::class], | |||
[MailSettingsController::class, Controller::class], |
@@ -1,164 +0,0 @@ | |||
<?php | |||
/** | |||
* @author Lukas Reschke <lukas@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\Settings\Controller; | |||
use OC\DB\Connection; | |||
use OC\Files\View; | |||
use OC\Settings\Controller\EncryptionController; | |||
use OCP\App\IAppManager; | |||
use OCA\Encryption\Migration; | |||
use OCP\IConfig; | |||
use OCP\IL10N; | |||
use OCP\ILogger; | |||
use OCP\IRequest; | |||
use OCP\IUserManager; | |||
use OCP\UserInterface; | |||
use Test\TestCase; | |||
/** | |||
* Class EncryptionControllerTest | |||
* | |||
* @package Tests\Settings\Controller | |||
*/ | |||
class EncryptionControllerTest extends TestCase { | |||
/** @var IRequest */ | |||
private $request; | |||
/** @var IL10N */ | |||
private $l10n; | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var Connection */ | |||
private $connection; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var View */ | |||
private $view; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var IAppManager */ | |||
private $appManager; | |||
/** @var EncryptionController */ | |||
private $encryptionController; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->request = $this->getMockBuilder(IRequest::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->l10n = $this->getMockBuilder(IL10N::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->l10n->expects($this->any()) | |||
->method('t') | |||
->will($this->returnCallback(function($message, array $replace) { | |||
return vsprintf($message, $replace); | |||
})); | |||
$this->config = $this->getMockBuilder(IConfig::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->connection = $this->getMockBuilder(Connection::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->userManager = $this->getMockBuilder(IUserManager::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->view = $this->getMockBuilder(View::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->logger = $this->getMockBuilder(ILogger::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->appManager = $this->getMockBuilder('\\OCP\\App\\IAppManager') | |||
->disableOriginalConstructor()->getMock(); | |||
$this->encryptionController = $this->getMockBuilder(EncryptionController::class) | |||
->setConstructorArgs([ | |||
'settings', | |||
$this->request, | |||
$this->l10n, | |||
$this->config, | |||
$this->connection, | |||
$this->userManager, | |||
$this->view, | |||
$this->logger, | |||
$this->appManager, | |||
]) | |||
->setMethods(['getMigration']) | |||
->getMock(); | |||
} | |||
public function testStartMigrationSuccessful() { | |||
// we need to be able to autoload the class we're mocking | |||
\OC_App::registerAutoloading('encryption', \OC_App::getAppPath('encryption')); | |||
$migration = $this->getMockBuilder(Migration::class) | |||
->disableOriginalConstructor()->getMock(); | |||
$this->encryptionController | |||
->expects($this->once()) | |||
->method('getMigration') | |||
->with($this->config, $this->view, $this->connection, $this->logger) | |||
->will($this->returnValue($migration)); | |||
$migration | |||
->expects($this->once()) | |||
->method('reorganizeSystemFolderStructure'); | |||
$migration | |||
->expects($this->once()) | |||
->method('updateDB'); | |||
$backend = $this->getMockBuilder(UserInterface::class) | |||
->getMock(); | |||
$this->userManager | |||
->expects($this->once()) | |||
->method('getBackends') | |||
->will($this->returnValue([$backend])); | |||
$backend | |||
->expects($this->once()) | |||
->method('getUsers') | |||
->will($this->returnValue(['User 1', 'User 2'])); | |||
$migration | |||
->expects($this->exactly(2)) | |||
->method('reorganizeFolderStructureForUser') | |||
->withConsecutive( | |||
['User 1'], | |||
['User 2'] | |||
); | |||
$migration | |||
->expects($this->once()) | |||
->method('finalCleanUp'); | |||
$expected = [ | |||
'data' => [ | |||
'message' => 'Migration Completed', | |||
], | |||
'status' => 'success', | |||
]; | |||
$this->assertSame($expected, $this->encryptionController->startMigration()); | |||
} | |||
public function testStartMigrationException() { | |||
$this->encryptionController | |||
->expects($this->once()) | |||
->method('getMigration') | |||
->with($this->config, $this->view, $this->connection, $this->logger) | |||
->will($this->throwException(new \Exception('My error message'))); | |||
$expected = [ | |||
'data' => [ | |||
'message' => 'A problem occurred, please check your log files (Error: My error message)', | |||
], | |||
'status' => 'error', | |||
]; | |||
$this->assertSame($expected, $this->encryptionController->startMigration()); | |||
} | |||
} |