@@ -11,6 +11,8 @@ | |||
/apps*/* | |||
!/apps/files | |||
!/apps/files_encryption | |||
!/apps/encryption | |||
!/apps/encryption_dummy | |||
!/apps/files_external | |||
!/apps/files_sharing | |||
!/apps/files_trashbin |
@@ -0,0 +1,6 @@ | |||
<?php | |||
$manager = \OC::$server->getEncryptionManager(); | |||
$module = new \OCA\Encryption_Dummy\DummyModule(); | |||
$manager->registerEncryptionModule($module); | |||
@@ -0,0 +1,20 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>encryption_dummy</id> | |||
<name>dummy encryption module</name> | |||
<description> | |||
This module does nothing, it is used for testing purpose only | |||
</description> | |||
<licence>AGPL</licence> | |||
<author>Bjoern Schiessle</author> | |||
<requiremin>8</requiremin> | |||
<shipped>true</shipped> | |||
<rememberlogin>false</rememberlogin> | |||
<types> | |||
<filesystem/> | |||
</types> | |||
<ocsid>166047</ocsid> | |||
<dependencies> | |||
<lib>openssl</lib> | |||
</dependencies> | |||
</info> |
@@ -0,0 +1 @@ | |||
0.0.1 |
@@ -0,0 +1,145 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OCA\Encryption_Dummy; | |||
class DummyModule implements \OCP\Encryption\IEncryptionModule { | |||
/** @var boolean */ | |||
protected $isWriteOperation; | |||
/** | |||
* @return string defining the technical unique id | |||
*/ | |||
public function getId() { | |||
return "34876934"; | |||
} | |||
/** | |||
* In comparison to getKey() this function returns a human readable (maybe translated) name | |||
* | |||
* @return string | |||
*/ | |||
public function getDisplayName() { | |||
return "Dummy Encryption Module"; | |||
} | |||
/** | |||
* start receiving chunks from a file. This is the place where you can | |||
* perform some initial step before starting encrypting/decrypting the | |||
* chunks | |||
* | |||
* @param string $path to the file | |||
* @param string $user who read/write the file (null for public access) | |||
* @param array $header contains the header data read from the file | |||
* @param array $accessList who has access to the file contains the key 'users' and 'public' | |||
* | |||
* $return array $header contain data as key-value pairs which should be | |||
* written to the header, in case of a write operation | |||
* or if no additional data is needed return a empty array | |||
*/ | |||
public function begin($path, $user, $header, $accessList) { | |||
return array(); | |||
} | |||
/** | |||
* last chunk received. This is the place where you can perform some final | |||
* operation and return some remaining data if something is left in your | |||
* buffer. | |||
* | |||
* @param string $path to the file | |||
* @return string remained data which should be written to the file in case | |||
* of a write operation | |||
*/ | |||
public function end($path) { | |||
if ($this->isWriteOperation) { | |||
$storage = \OC::$server->getEncryptionKeyStorage($this->getId()); | |||
$storage->setFileKey($path, 'fileKey', 'foo'); | |||
} | |||
return ''; | |||
} | |||
/** | |||
* encrypt data | |||
* | |||
* @param string $data you want to encrypt | |||
* @return mixed encrypted data | |||
*/ | |||
public function encrypt($data) { | |||
$this->isWriteOperation = true; | |||
return $data; | |||
} | |||
/** | |||
* decrypt data | |||
* | |||
* @param string $data you want to decrypt | |||
* @param string $user decrypt as user (null for public access) | |||
* @return mixed decrypted data | |||
*/ | |||
public function decrypt($data) { | |||
$this->isWriteOperation=false; | |||
return $data; | |||
} | |||
/** | |||
* update encrypted file, e.g. give additional users access to the file | |||
* | |||
* @param string $path path to the file which should be updated | |||
* @param array $accessList who has access to the file contains the key 'users' and 'public' | |||
* @return boolean | |||
*/ | |||
public function update($path, $accessList) { | |||
return true; | |||
} | |||
/** | |||
* should the file be encrypted or not | |||
* | |||
* @param string $path | |||
* @return boolean | |||
*/ | |||
public function shouldEncrypt($path) { | |||
if (strpos($path, '/'. \OCP\User::getUser() . '/files/') === 0) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* calculate unencrypted size | |||
* | |||
* @param string $path to file | |||
* @return integer unencrypted size | |||
*/ | |||
public function calculateUnencryptedSize($path) { | |||
return 42; | |||
} | |||
public function getUnencryptedBlockSize() { | |||
return 6126; | |||
} | |||
} |
@@ -148,7 +148,7 @@ class OC { | |||
// search the 3rdparty folder | |||
OC::$THIRDPARTYROOT = OC_Config::getValue('3rdpartyroot', null); | |||
OC::$THIRDPARTYWEBROOT = OC_Config::getValue('3rdpartyurl', null); | |||
if (empty(OC::$THIRDPARTYROOT) && empty(OC::$THIRDPARTYWEBROOT)) { | |||
if (file_exists(OC::$SERVERROOT . '/3rdparty')) { | |||
OC::$THIRDPARTYROOT = OC::$SERVERROOT; | |||
@@ -163,7 +163,7 @@ class OC { | |||
. ' folder in the ownCloud folder or the folder above.' | |||
. ' You can also configure the location in the config.php file.'); | |||
} | |||
// search the apps folder | |||
$config_paths = OC_Config::getValue('apps_paths', array()); | |||
if (!empty($config_paths)) { | |||
@@ -613,6 +613,8 @@ class OC { | |||
self::registerShareHooks(); | |||
self::registerLogRotate(); | |||
self::registerLocalAddressBook(); | |||
self::registerEncryptionWrapper(); | |||
self::registerEncryptionHooks(); | |||
//make sure temporary files are cleaned up | |||
$tmpManager = \OC::$server->getTempManager(); | |||
@@ -669,6 +671,45 @@ class OC { | |||
}); | |||
} | |||
private static function registerEncryptionWrapper() { | |||
$enabled = self::$server->getEncryptionManager()->isEnabled(); | |||
if ($enabled) { | |||
\OC\Files\Filesystem::addStorageWrapper('oc_encryption', function ($mountPoint, $storage) { | |||
$parameters = array('storage' => $storage, 'mountPoint' => $mountPoint); | |||
$manager = \OC::$server->getEncryptionManager(); | |||
$util = new \OC\Encryption\Util(new \OC\Files\View(), \OC::$server->getUserManager()); | |||
$user = \OC::$server->getUserSession()->getUser(); | |||
$logger = \OC::$server->getLogger(); | |||
$uid = $user ? $user->getUID() : null; | |||
return new \OC\Files\Storage\Wrapper\Encryption($parameters, $manager,$util, $logger, $uid); | |||
}); | |||
} | |||
} | |||
private static function registerEncryptionHooks() { | |||
$enabled = self::$server->getEncryptionManager()->isEnabled(); | |||
if ($enabled) { | |||
$user = \OC::$server->getUserSession()->getUser(); | |||
$uid = ''; | |||
if ($user) { | |||
$uid = $user->getUID(); | |||
} | |||
$updater = new \OC\Encryption\Update( | |||
new \OC\Files\View(), | |||
new \OC\Encryption\Util(new \OC\Files\View(), \OC::$server->getUserManager()), | |||
\OC\Files\Filesystem::getMountManager(), | |||
\OC::$server->getEncryptionManager(), | |||
$uid | |||
); | |||
\OCP\Util::connectHook('OCP\Share', 'post_shared', $updater, 'postShared'); | |||
\OCP\Util::connectHook('OCP\Share', 'post_unshare', $updater, 'postUnshared'); | |||
//\OCP\Util::connectHook('OC_Filesystem', 'post_umount', 'OCA\Files_Encryption\Hooks', 'postUnmount'); | |||
//\OCP\Util::connectHook('OC_Filesystem', 'umount', 'OCA\Files_Encryption\Hooks', 'preUnmount'); | |||
} | |||
} | |||
/** | |||
* register hooks for the cache | |||
*/ |
@@ -0,0 +1,29 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption\Exceptions; | |||
class EncryptionHeaderKeyExistsException extends \Exception { | |||
} |
@@ -0,0 +1,28 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption\Exceptions; | |||
class ModuleAlreadyExistsException extends \Exception { | |||
} |
@@ -0,0 +1,28 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption\Exceptions; | |||
class ModuleDoesNotExistsException extends \Exception { | |||
} |
@@ -0,0 +1,52 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption\Keys; | |||
use OC\Encryption\Util; | |||
use OC\Files\View; | |||
use OC\User; | |||
/** | |||
* Factory provides KeyStorage for different encryption modules | |||
*/ | |||
class Factory { | |||
/** @var array */ | |||
protected $instances = array(); | |||
/** | |||
* get a KeyStorage instance | |||
* | |||
* @param string $encryptionModuleId | |||
* @param View $view | |||
* @param Util $util | |||
* @return Storage | |||
*/ | |||
public function get($encryptionModuleId,View $view, Util $util) { | |||
if (!isset($this->instances[$encryptionModuleId])) { | |||
$this->instances[$encryptionModuleId] = new Storage($encryptionModuleId, $view, $util); | |||
} | |||
return $this->instances[$encryptionModuleId]; | |||
} | |||
} |
@@ -0,0 +1,320 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption\Keys; | |||
use OC\Encryption\Util; | |||
use OC\Files\View; | |||
use OCA\Files_Encryption\Exception\EncryptionException; | |||
class Storage implements \OCP\Encryption\Keys\IStorage { | |||
/** @var View */ | |||
private $view; | |||
/** @var Util */ | |||
private $util; | |||
// base dir where all the file related keys are stored | |||
private $keys_base_dir; | |||
private $encryption_base_dir; | |||
private $keyCache = array(); | |||
/** @var string */ | |||
private $encryptionModuleId; | |||
/** | |||
* @param string $encryptionModuleId | |||
* @param View $view | |||
* @param Util $util | |||
*/ | |||
public function __construct($encryptionModuleId, View $view, Util $util) { | |||
$this->view = $view; | |||
$this->util = $util; | |||
$this->encryptionModuleId = $encryptionModuleId; | |||
$this->encryption_base_dir = '/files_encryption'; | |||
$this->keys_base_dir = $this->encryption_base_dir .'/keys'; | |||
} | |||
/** | |||
* get user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want the key | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getUserKey($uid, $keyId) { | |||
$path = $this->constructUserKeyPath($keyId, $uid); | |||
return $this->getKey($path); | |||
} | |||
/** | |||
* get file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getFileKey($path, $keyId) { | |||
$keyDir = $this->getFileKeyDir($path); | |||
return $this->getKey($keyDir . $keyId); | |||
} | |||
/** | |||
* get system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getSystemUserKey($keyId) { | |||
$path = $this->constructUserKeyPath($keyId); | |||
return $this->getKey($path); | |||
} | |||
/** | |||
* set user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want the key | |||
* @param string $keyId id of the key | |||
* @param mixed $key | |||
*/ | |||
public function setUserKey($uid, $keyId, $key) { | |||
$path = $this->constructUserKeyPath($keyId, $uid); | |||
return $this->setKey($path, $key); | |||
} | |||
/** | |||
* set file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* @param boolean | |||
*/ | |||
public function setFileKey($path, $keyId, $key) { | |||
$keyDir = $this->getFileKeyDir($path); | |||
return $this->setKey($keyDir . $keyId, $key); | |||
} | |||
/** | |||
* set system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* @param mixed $key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function setSystemUserKey($keyId, $key) { | |||
$path = $this->constructUserKeyPath($keyId); | |||
return $this->setKey($path, $key); | |||
} | |||
/** | |||
* delete user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want to delete the key | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteUserKey($uid, $keyId) { | |||
$path = $this->constructUserKeyPath($keyId, $uid); | |||
return $this->view->unlink($path); | |||
} | |||
/** | |||
* delete file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteFileKey($path, $keyId) { | |||
$keyDir = $this->getFileKeyDir($path); | |||
return $this->view->unlink($keyDir . $keyId); | |||
} | |||
/** | |||
* delete all file keys for a given file | |||
* | |||
* @param string $path to the file | |||
* @return boolean | |||
*/ | |||
public function deleteAllFileKeys($path) { | |||
$keyDir = $this->getFileKeyDir($path); | |||
return $this->view->deleteAll(dirname($keyDir)); | |||
} | |||
/** | |||
* delete system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteSystemUserKey($keyId) { | |||
$path = $this->constructUserKeyPath($keyId); | |||
return $this->view->unlink($path); | |||
} | |||
/** | |||
* construct path to users key | |||
* | |||
* @param string $keyId | |||
* @param string $uid | |||
* @return string | |||
*/ | |||
protected function constructUserKeyPath($keyId, $uid = null) { | |||
if ($uid === null) { | |||
$path = $this->encryption_base_dir . '/' . $this->encryptionModuleId . '/' . $keyId; | |||
} else { | |||
$path = '/' . $uid . $this->encryption_base_dir . '/' | |||
. $this->encryptionModuleId . '/' . $uid . '.' . $keyId; | |||
} | |||
return $path; | |||
} | |||
/** | |||
* read key from hard disk | |||
* | |||
* @param string $path to key | |||
* @return string | |||
*/ | |||
private function getKey($path) { | |||
$key = ''; | |||
if ($this->view->file_exists($path)) { | |||
if (isset($this->keyCache[$path])) { | |||
$key = $this->keyCache[$path]; | |||
} else { | |||
$key = $this->view->file_get_contents($path); | |||
$this->keyCache[$path] = $key; | |||
} | |||
} | |||
return $key; | |||
} | |||
/** | |||
* write key to disk | |||
* | |||
* | |||
* @param string $path path to key directory | |||
* @param string $key key | |||
* @return bool | |||
*/ | |||
private function setKey($path, $key) { | |||
$this->keySetPreparation(dirname($path)); | |||
$result = $this->view->file_put_contents($path, $key); | |||
if (is_int($result) && $result > 0) { | |||
$this->keyCache[$path] = $key; | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* get path to key folder for a given file | |||
* | |||
* @param string $path path to the file, relative to data/ | |||
* @return string | |||
* @throws EncryptionException | |||
* @internal param string $keyId | |||
*/ | |||
private function getFileKeyDir($path) { | |||
if ($this->view->is_dir($path)) { | |||
throw new EncryptionException('file was expected but directory was given', EncryptionException::GENERIC); | |||
} | |||
list($owner, $filename) = $this->util->getUidAndFilename($path); | |||
$filename = $this->util->stripPartialFileExtension($filename); | |||
// in case of system wide mount points the keys are stored directly in the data directory | |||
if ($this->util->isSystemWideMountPoint($filename)) { | |||
$keyPath = $this->keys_base_dir . $filename . '/'; | |||
} else { | |||
$keyPath = '/' . $owner . $this->keys_base_dir . $filename . '/'; | |||
} | |||
return \OC\Files\Filesystem::normalizePath($keyPath . $this->encryptionModuleId . '/', false); | |||
} | |||
/** | |||
* move keys if a file was renamed | |||
* | |||
* @param string $source | |||
* @param string $target | |||
* @param string $owner | |||
* @param bool $systemWide | |||
*/ | |||
public function renameKeys($source, $target, $owner, $systemWide) { | |||
if ($systemWide) { | |||
$sourcePath = $this->keys_base_dir . $source . '/'; | |||
$targetPath = $this->keys_base_dir . $target . '/'; | |||
} else { | |||
$sourcePath = '/' . $owner . $this->keys_base_dir . $source . '/'; | |||
$targetPath = '/' . $owner . $this->keys_base_dir . $target . '/'; | |||
} | |||
if ($this->view->file_exists($sourcePath)) { | |||
$this->keySetPreparation(dirname($targetPath)); | |||
$this->view->rename($sourcePath, $targetPath); | |||
} | |||
} | |||
/** | |||
* Make preparations to filesystem for saving a keyfile | |||
* | |||
* @param string $path relative to the views root | |||
*/ | |||
protected function keySetPreparation($path) { | |||
// If the file resides within a subdirectory, create it | |||
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); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,170 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption; | |||
use OCP\Encryption\IEncryptionModule; | |||
class Manager implements \OCP\Encryption\IManager { | |||
/** @var array */ | |||
protected $encryptionModules; | |||
/** @var \OCP\IConfig */ | |||
protected $config; | |||
/** | |||
* @param \OCP\IConfig $config | |||
*/ | |||
public function __construct(\OCP\IConfig $config) { | |||
$this->encryptionModules = array(); | |||
$this->config = $config; | |||
} | |||
/** | |||
* Check if encryption is enabled | |||
* | |||
* @return bool true if enabled, false if not | |||
*/ | |||
public function isEnabled() { | |||
$installed = $this->config->getSystemValue('installed', false); | |||
if (!$installed) { | |||
return false; | |||
} | |||
$enabled = $this->config->getAppValue('core', 'encryption_enabled', 'no'); | |||
return $enabled === 'yes'; | |||
} | |||
/** | |||
* Registers an encryption module | |||
* | |||
* @param IEncryptionModule $module | |||
* @throws Exceptions\ModuleAlreadyExistsException | |||
*/ | |||
public function registerEncryptionModule(IEncryptionModule $module) { | |||
$id = $module->getId(); | |||
$name = $module->getDisplayName(); | |||
if (isset($this->encryptionModules[$id])) { | |||
$message = 'Id "' . $id . '" already used by encryption module "' . $name . '"'; | |||
throw new Exceptions\ModuleAlreadyExistsException($message); | |||
} | |||
$defaultEncryptionModuleId = $this->getDefaultEncryptionModuleId(); | |||
if (empty($defaultEncryptionModuleId)) { | |||
$this->setDefaultEncryptionModule($id); | |||
} | |||
$this->encryptionModules[$id] = $module; | |||
} | |||
/** | |||
* Unregisters an encryption module | |||
* | |||
* @param IEncryptionModule $module | |||
*/ | |||
public function unregisterEncryptionModule(IEncryptionModule $module) { | |||
unset($this->encryptionModules[$module->getId()]); | |||
} | |||
/** | |||
* get a list of all encryption modules | |||
* | |||
* @return IEncryptionModule[] | |||
*/ | |||
public function getEncryptionModules() { | |||
return $this->encryptionModules; | |||
} | |||
/** | |||
* get a specific encryption module | |||
* | |||
* @param string $moduleId | |||
* @return IEncryptionModule | |||
* @throws Exceptions\ModuleDoesNotExistsException | |||
*/ | |||
public function getEncryptionModule($moduleId) { | |||
if (isset($this->encryptionModules[$moduleId])) { | |||
return $this->encryptionModules[$moduleId]; | |||
} else { | |||
$message = "Module with id: $moduleId does not exists."; | |||
throw new Exceptions\ModuleDoesNotExistsException($message); | |||
} | |||
} | |||
/** | |||
* get default encryption module | |||
* | |||
* @return \OCP\Encryption\IEncryptionModule | |||
* @throws Exceptions\ModuleDoesNotExistsException | |||
*/ | |||
public function getDefaultEncryptionModule() { | |||
$defaultModuleId = $this->getDefaultEncryptionModuleId(); | |||
if (!empty($defaultModuleId)) { | |||
if (isset($this->encryptionModules[$defaultModuleId])) { | |||
return $this->encryptionModules[$defaultModuleId]; | |||
} else { | |||
$message = 'Default encryption module not loaded'; | |||
throw new Exceptions\ModuleDoesNotExistsException($message); | |||
} | |||
} else { | |||
$message = 'No default encryption module defined'; | |||
throw new Exceptions\ModuleDoesNotExistsException($message); | |||
} | |||
} | |||
/** | |||
* set default encryption module Id | |||
* | |||
* @param string $moduleId | |||
* @return bool | |||
*/ | |||
public function setDefaultEncryptionModule($moduleId) { | |||
try { | |||
$this->config->setAppValue('core', 'default_encryption_module', $moduleId); | |||
return true; | |||
} catch (\Exception $e) { | |||
return false; | |||
} | |||
} | |||
/** | |||
* get default encryption module Id | |||
* | |||
* @return string | |||
*/ | |||
protected function getDefaultEncryptionModuleId() { | |||
try { | |||
return $this->config->getAppValue('core', 'default_encryption_module'); | |||
} catch (\Exception $e) { | |||
return ''; | |||
} | |||
} | |||
} |
@@ -0,0 +1,111 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption; | |||
use \OC\Files\Mount; | |||
use \OC\Files\View; | |||
/** | |||
* update encrypted files, e.g. because a file was shared | |||
*/ | |||
class Update { | |||
/** @var \OC\Files\View */ | |||
protected $view; | |||
/** @var \OC\Encryption\Util */ | |||
protected $util; | |||
/** @var \OC\Files\Mount\Manager */ | |||
protected $mountManager; | |||
/** @var \OC\Encryption\Manager */ | |||
protected $encryptionManager; | |||
/** @var string */ | |||
protected $uid; | |||
/** | |||
* | |||
* @param \OC\Files\View $view | |||
* @param \OC\Encryption\Util $util | |||
* @param \OC\Files\Mount\Manager $mountManager | |||
* @param \OC\Encryption\Manager $encryptionManager | |||
* @param string $uid | |||
*/ | |||
public function __construct( | |||
View $view, | |||
Util $util, | |||
Mount\Manager $mountManager, | |||
Manager $encryptionManager, | |||
$uid | |||
) { | |||
$this->view = $view; | |||
$this->util = $util; | |||
$this->mountManager = $mountManager; | |||
$this->encryptionManager = $encryptionManager; | |||
$this->uid = $uid; | |||
} | |||
public function postShared($params) { | |||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { | |||
$this->update($params['fileSource']); | |||
} | |||
} | |||
public function postUnshared($params) { | |||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { | |||
$this->update($params['fileSource']); | |||
} | |||
} | |||
/** | |||
* update keyfiles and share keys recursively | |||
* | |||
* @param int $fileSource file source id | |||
*/ | |||
private function update($fileSource) { | |||
$path = \OC\Files\Filesystem::getPath($fileSource); | |||
$absPath = '/' . $this->uid . '/files' . $path; | |||
$mount = $this->mountManager->find($path); | |||
$mountPoint = $mount->getMountPoint(); | |||
// if a folder was shared, get a list of all (sub-)folders | |||
if ($this->view->is_dir($absPath)) { | |||
$allFiles = $this->util->getAllFiles($absPath, $mountPoint); | |||
} else { | |||
$allFiles = array($absPath); | |||
} | |||
$encryptionModule = $this->encryptionManager->getDefaultEncryptionModule(); | |||
foreach ($allFiles as $path) { | |||
$usersSharing = $this->util->getSharingUsersArray($path); | |||
$encryptionModule->update($absPath, $usersSharing); | |||
} | |||
} | |||
} |
@@ -0,0 +1,401 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Encryption; | |||
use OC\Encryption\Exceptions\EncryptionHeaderToLargeException; | |||
use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; | |||
use OCP\Encryption\IEncryptionModule; | |||
class Util { | |||
const HEADER_START = 'HBEGIN'; | |||
const HEADER_END = 'HEND'; | |||
const HEADER_PADDING_CHAR = '-'; | |||
const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module'; | |||
/** | |||
* block size will always be 8192 for a PHP stream | |||
* @see https://bugs.php.net/bug.php?id=21641 | |||
* @var integer | |||
*/ | |||
protected $headerSize = 8192; | |||
/** | |||
* block size will always be 8192 for a PHP stream | |||
* @see https://bugs.php.net/bug.php?id=21641 | |||
* @var integer | |||
*/ | |||
protected $blockSize = 8192; | |||
/** @var \OC\Files\View */ | |||
protected $view; | |||
/** @var array */ | |||
protected $ocHeaderKeys; | |||
/** @var \OC\User\Manager */ | |||
protected $userManager; | |||
/** @var array paths excluded from encryption */ | |||
protected $excludedPaths; | |||
/** | |||
* @param \OC\Files\View $view root view | |||
*/ | |||
public function __construct(\OC\Files\View $view, \OC\User\Manager $userManager) { | |||
$this->ocHeaderKeys = [ | |||
self::HEADER_ENCRYPTION_MODULE_KEY | |||
]; | |||
$this->view = $view; | |||
$this->userManager = $userManager; | |||
$this->excludedPaths[] = 'files_encryption'; | |||
} | |||
/** | |||
* read encryption module ID from header | |||
* | |||
* @param array $header | |||
* @return string | |||
*/ | |||
public function getEncryptionModuleId(array $header) { | |||
$id = ''; | |||
$encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY; | |||
if (isset($header[$encryptionModuleKey])) { | |||
$id = $header[$encryptionModuleKey]; | |||
} | |||
return $id; | |||
} | |||
/** | |||
* read header into array | |||
* | |||
* @param string $header | |||
* @return array | |||
*/ | |||
public function readHeader($header) { | |||
$result = array(); | |||
if (substr($header, 0, strlen(self::HEADER_START)) === self::HEADER_START) { | |||
$endAt = strpos($header, self::HEADER_END); | |||
if ($endAt !== false) { | |||
$header = substr($header, 0, $endAt + strlen(self::HEADER_END)); | |||
// +1 to not start with an ':' which would result in empty element at the beginning | |||
$exploded = explode(':', substr($header, strlen(self::HEADER_START)+1)); | |||
$element = array_shift($exploded); | |||
while ($element !== self::HEADER_END) { | |||
$result[$element] = array_shift($exploded); | |||
$element = array_shift($exploded); | |||
} | |||
} | |||
} | |||
return $result; | |||
} | |||
/** | |||
* create header for encrypted file | |||
* | |||
* @param array $headerData | |||
* @param IEncryptionModule $encryptionModule | |||
* @return string | |||
* @throws EncryptionHeaderToLargeException if header has to many arguments | |||
* @throws EncryptionHeaderKeyExistsException if header key is already in use | |||
*/ | |||
public function createHeader(array $headerData, IEncryptionModule $encryptionModule) { | |||
$header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':'; | |||
foreach ($headerData as $key => $value) { | |||
if (in_array($key, $this->ocHeaderKeys)) { | |||
throw new EncryptionHeaderKeyExistsException('header key "'. $key . '" already reserved by ownCloud'); | |||
} | |||
$header .= $key . ':' . $value . ':'; | |||
} | |||
$header .= self::HEADER_END; | |||
if (strlen($header) > $this->getHeaderSize()) { | |||
throw new EncryptionHeaderToLargeException('max header size exceeded', EncryptionException::ENCRYPTION_HEADER_TO_LARGE); | |||
} | |||
$paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT); | |||
return $paddedHeader; | |||
} | |||
/** | |||
* Find, sanitise and format users sharing a file | |||
* @note This wraps other methods into a portable bundle | |||
* @param string $path path relative to current users files folder | |||
* @return array | |||
*/ | |||
public function getSharingUsersArray($path) { | |||
// Make sure that a share key is generated for the owner too | |||
list($owner, $ownerPath) = $this->getUidAndFilename($path); | |||
// always add owner to the list of users with access to the file | |||
$userIds = array($owner); | |||
if (!$this->isFile($ownerPath)) { | |||
return array('users' => $userIds, 'public' => false); | |||
} | |||
$ownerPath = substr($ownerPath, strlen('/files')); | |||
$ownerPath = $this->stripPartialFileExtension($ownerPath); | |||
// Find out who, if anyone, is sharing the file | |||
$result = \OCP\Share::getUsersSharingFile($ownerPath, $owner); | |||
$userIds = \array_merge($userIds, $result['users']); | |||
$public = $result['public'] || $result['remote']; | |||
// check if it is a group mount | |||
if (\OCP\App::isEnabled("files_external")) { | |||
$mounts = \OC_Mount_Config::getSystemMountPoints(); | |||
foreach ($mounts as $mount) { | |||
if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) { | |||
$mountedFor = $this->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups']); | |||
$userIds = array_merge($userIds, $mountedFor); | |||
} | |||
} | |||
} | |||
// Remove duplicate UIDs | |||
$uniqueUserIds = array_unique($userIds); | |||
return array('users' => $uniqueUserIds, 'public' => $public); | |||
} | |||
/** | |||
* go recursively through a dir and collect all files and sub files. | |||
* | |||
* @param string $dir relative to the users files folder | |||
* @param strinf $mountPoint | |||
* @return array with list of files relative to the users files folder | |||
*/ | |||
public function getAllFiles($dir, $mountPoint = '') { | |||
$result = array(); | |||
$dirList = array($dir); | |||
while ($dirList) { | |||
$dir = array_pop($dirList); | |||
$content = $this->view->getDirectoryContent($dir); | |||
foreach ($content as $c) { | |||
// getDirectoryContent() returns the paths relative to the mount points, so we need | |||
// to re-construct the complete path | |||
$path = ($mountPoint !== '') ? $mountPoint . '/' . $c['path'] : $c['path']; | |||
if ($c['type'] === 'dir') { | |||
$dirList[] = $path; | |||
} else { | |||
$result[] = $path; | |||
} | |||
} | |||
} | |||
return $result; | |||
} | |||
/** | |||
* check if it is a file uploaded by the user stored in data/user/files | |||
* or a metadata file | |||
* | |||
* @param string $path | |||
* @return boolean | |||
*/ | |||
protected function isFile($path) { | |||
if (substr($path, 0, strlen('/files/')) === '/files/') { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* return size of encryption header | |||
* | |||
* @return integer | |||
*/ | |||
public function getHeaderSize() { | |||
return $this->headerSize; | |||
} | |||
/** | |||
* return size of block read by a PHP stream | |||
* | |||
* @return integer | |||
*/ | |||
public function getBlockSize() { | |||
return $this->blockSize; | |||
} | |||
/** | |||
* get the owner and the path for the owner | |||
* | |||
* @param string $path | |||
* @return array | |||
* @throws \BadMethodCallException | |||
*/ | |||
public function getUidAndFilename($path) { | |||
$parts = explode('/', $path); | |||
$uid = ''; | |||
if (count($parts) > 2) { | |||
$uid = $parts[1]; | |||
} | |||
if (!$this->userManager->userExists($uid)) { | |||
throw new \BadMethodCallException('path needs to be relative to the system wide data folder and point to a user specific file'); | |||
} | |||
$pathinfo = pathinfo($path); | |||
$partfile = false; | |||
$parentFolder = false; | |||
if (array_key_exists('extension', $pathinfo) && $pathinfo['extension'] === 'part') { | |||
// if the real file exists we check this file | |||
$filePath = $pathinfo['dirname'] . '/' . $pathinfo['filename']; | |||
if ($this->view->file_exists($filePath)) { | |||
$pathToCheck = $pathinfo['dirname'] . '/' . $pathinfo['filename']; | |||
} else { // otherwise we look for the parent | |||
$pathToCheck = $pathinfo['dirname']; | |||
$parentFolder = true; | |||
} | |||
$partfile = true; | |||
} else { | |||
$pathToCheck = $path; | |||
} | |||
$pathToCheck = substr($pathToCheck, strlen('/' . $uid)); | |||
$this->view->chroot('/' . $uid); | |||
$owner = $this->view->getOwner($pathToCheck); | |||
// Check that UID is valid | |||
if (!$this->userManager->userExists($owner)) { | |||
throw new \BadMethodCallException('path needs to be relative to the system wide data folder and point to a user specific file'); | |||
} | |||
\OC\Files\Filesystem::initMountPoints($owner); | |||
$info = $this->view->getFileInfo($pathToCheck); | |||
$this->view->chroot('/' . $owner); | |||
$ownerPath = $this->view->getPath($info->getId()); | |||
$this->view->chroot('/'); | |||
if ($parentFolder) { | |||
$ownerPath = $ownerPath . '/'. $pathinfo['filename']; | |||
} | |||
if ($partfile) { | |||
$ownerPath = $ownerPath . '.' . $pathinfo['extension']; | |||
} | |||
return array( | |||
$owner, | |||
\OC\Files\Filesystem::normalizePath($ownerPath) | |||
); | |||
} | |||
/** | |||
* Remove .path extension from a file path | |||
* @param string $path Path that may identify a .part file | |||
* @return string File path without .part extension | |||
* @note this is needed for reusing keys | |||
*/ | |||
public function stripPartialFileExtension($path) { | |||
$extension = pathinfo($path, PATHINFO_EXTENSION); | |||
if ( $extension === 'part') { | |||
$newLength = strlen($path) - 5; // 5 = strlen(".part") | |||
$fPath = substr($path, 0, $newLength); | |||
// if path also contains a transaction id, we remove it too | |||
$extension = pathinfo($fPath, PATHINFO_EXTENSION); | |||
if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId") | |||
$newLength = strlen($fPath) - strlen($extension) -1; | |||
$fPath = substr($fPath, 0, $newLength); | |||
} | |||
return $fPath; | |||
} else { | |||
return $path; | |||
} | |||
} | |||
protected function getUserWithAccessToMountPoint($users, $groups) { | |||
$result = array(); | |||
if (in_array('all', $users)) { | |||
$result = \OCP\User::getUsers(); | |||
} else { | |||
$result = array_merge($result, $users); | |||
foreach ($groups as $group) { | |||
$result = array_merge($result, \OC_Group::usersInGroup($group)); | |||
} | |||
} | |||
return $result; | |||
} | |||
/** | |||
* check if the file is stored on a system wide mount point | |||
* @param string $path relative to /data/user with leading '/' | |||
* @return boolean | |||
*/ | |||
public function isSystemWideMountPoint($path) { | |||
$normalizedPath = ltrim($path, '/'); | |||
if (\OCP\App::isEnabled("files_external")) { | |||
$mounts = \OC_Mount_Config::getSystemMountPoints(); | |||
foreach ($mounts as $mount) { | |||
if ($mount['mountpoint'] == substr($normalizedPath, 0, strlen($mount['mountpoint']))) { | |||
if ($this->isMountPointApplicableToUser($mount)) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* check if it is a path which is excluded by ownCloud from encryption | |||
* | |||
* @param string $path | |||
* @return boolean | |||
*/ | |||
public function isExcluded($path) { | |||
$root = explode('/', $path, 2); | |||
if (isset($root[0])) { | |||
if (in_array($root[0], $this->excludedPaths)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -148,6 +148,13 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { | |||
return $this->data['encrypted']; | |||
} | |||
/** | |||
* @return int | |||
*/ | |||
public function getUnencryptedSize() { | |||
return isset($this->data['unencrypted_size']) ? $this->data['unencrypted_size'] : 0; | |||
} | |||
/** | |||
* @return int | |||
*/ |
@@ -0,0 +1,319 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Files\Storage\Wrapper; | |||
use OC\Encryption\Exceptions\ModuleDoesNotExistsException; | |||
class Encryption extends Wrapper { | |||
/** @var string */ | |||
private $mountPoint; | |||
/** @var \OC\Encryption\Util */ | |||
private $util; | |||
/** @var \OC\Encryption\Manager */ | |||
private $encryptionManager; | |||
/** @var \OC\Log */ | |||
private $logger; | |||
/** @var string */ | |||
private $uid; | |||
/** @var array */ | |||
private $unencryptedSize; | |||
/** | |||
* @param array $parameters | |||
* @param \OC\Encryption\Manager $encryptionManager | |||
* @param \OC\Encryption\Util $util | |||
* @param \OC\Log $logger | |||
* @param string $uid user who perform the read/write operation (null for public access) | |||
*/ | |||
public function __construct( | |||
$parameters, | |||
\OC\Encryption\Manager $encryptionManager = null, | |||
\OC\Encryption\Util $util = null, | |||
\OC\Log $logger = null, | |||
$uid = null | |||
) { | |||
$this->mountPoint = $parameters['mountPoint']; | |||
$this->encryptionManager = $encryptionManager; | |||
$this->util = $util; | |||
$this->logger = $logger; | |||
$this->uid = $uid; | |||
$this->unencryptedSize = array(); | |||
parent::__construct($parameters); | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.filesize.php | |||
* The result for filesize when called on a folder is required to be 0 | |||
* | |||
* @param string $path | |||
* @return int | |||
*/ | |||
public function filesize($path) { | |||
$size = 0; | |||
$fullPath = $this->getFullPath($path); | |||
$encryptedSize = $this->storage->filesize($path); | |||
$info = $this->getCache()->get($path); | |||
if($encryptedSize > 0 && $info['encrypted']) { | |||
$size = $info['unencrypted_size']; | |||
if ($size <= 0) { | |||
$encryptionModule = $this->getEncryptionModule($path); | |||
if ($encryptionModule) { | |||
$size = $encryptionModule->calculateUnencryptedSize($fullPath); | |||
$this->getCache()->update($info['fileid'], array('unencrypted_size' => $size)); | |||
} | |||
} | |||
} else if (isset($this->unencryptedSize[$fullPath]) && isset($info['fileid'])) { | |||
$size = $this->unencryptedSize[$fullPath]; | |||
$info['encrypted'] = true; | |||
$info['unencrypted_size'] = $size; | |||
$info['size'] = $encryptedSize; | |||
$this->getCache()->put($path, $info); | |||
} | |||
return $size; | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.file_get_contents.php | |||
* | |||
* @param string $path | |||
* @return string | |||
*/ | |||
public function file_get_contents($path) { | |||
$data = null; | |||
$encryptionModule = $this->getEncryptionModule($path); | |||
if ($encryptionModule) { | |||
$handle = $this->fopen($path, 'r'); | |||
if (is_resource($handle)) { | |||
while (!feof($handle)) { | |||
$data .= fread($handle, $this->util->getBlockSize()); | |||
} | |||
} | |||
} else { | |||
$data = $this->storage->file_get_contents($path); | |||
} | |||
return $data; | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.file_put_contents.php | |||
* | |||
* @param string $path | |||
* @param string $data | |||
* @return bool | |||
*/ | |||
public function file_put_contents($path, $data) { | |||
// file put content will always be translated to a stream write | |||
$handle = $this->fopen($path, 'w'); | |||
fwrite($handle, $data); | |||
return fclose($handle); | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.unlink.php | |||
* | |||
* @param string $path | |||
* @return bool | |||
*/ | |||
public function unlink($path) { | |||
if ($this->util->isExcluded($path)) { | |||
return $this->storage->unlink($path); | |||
} | |||
$encryptionModule = $this->getEncryptionModule($path); | |||
if ($encryptionModule) { | |||
$keyStorage = \OC::$server->getEncryptionKeyStorage($encryptionModule->getId()); | |||
$keyStorage->deleteAllFileKeys($this->getFullPath($path)); | |||
} | |||
return $this->storage->unlink($path); | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.rename.php | |||
* | |||
* @param string $path1 | |||
* @param string $path2 | |||
* @return bool | |||
*/ | |||
public function rename($path1, $path2) { | |||
if ($this->util->isExcluded($path1)) { | |||
return $this->storage->rename($path1, $path2); | |||
} | |||
$fullPath1 = $this->getFullPath($path1); | |||
list($owner, $source) = $this->util->getUidAndFilename($fullPath1); | |||
$result = $this->storage->rename($path1, $path2); | |||
if ($result) { | |||
$fullPath2 = $this->getFullPath($path2); | |||
$systemWide = $this->util->isSystemWideMountPoint($this->mountPoint); | |||
list(, $target) = $this->util->getUidAndFilename($fullPath2); | |||
$encryptionModule = $this->getEncryptionModule($path2); | |||
if ($encryptionModule) { | |||
$keyStorage = \OC::$server->getEncryptionKeyStorage($encryptionModule->getId()); | |||
$keyStorage->renameKeys($source, $target, $owner, $systemWide); | |||
} | |||
} | |||
return $result; | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.copy.php | |||
* | |||
* @param string $path1 | |||
* @param string $path2 | |||
* @return bool | |||
*/ | |||
public function copy($path1, $path2) { | |||
// todo copy encryption keys, get users with access to the file and reencrypt | |||
// or is this to encryption module specific? Then we can hand this over | |||
return $this->storage->copy($path1, $path2); | |||
} | |||
/** | |||
* see http://php.net/manual/en/function.fopen.php | |||
* | |||
* @param string $path | |||
* @param string $mode | |||
* @return resource | |||
*/ | |||
public function fopen($path, $mode) { | |||
$shouldEncrypt = false; | |||
$encryptionModule = null; | |||
$header = $this->getHeader($path); | |||
$fullPath = $this->getFullPath($path); | |||
$encryptionModuleId = $this->util->getEncryptionModuleId($header); | |||
$size = $unencryptedSize = 0; | |||
if ($this->file_exists($path)) { | |||
$size = $this->storage->filesize($path); | |||
$unencryptedSize = $this->filesize($path); | |||
} | |||
try { | |||
if ( | |||
$mode === 'w' | |||
|| $mode === 'w+' | |||
|| $mode === 'wb' | |||
|| $mode === 'wb+' | |||
) { | |||
if (!empty($encryptionModuleId)) { | |||
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); | |||
} else { | |||
$encryptionModule = $this->encryptionManager->getDefaultEncryptionModule(); | |||
} | |||
$shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath); | |||
} else { | |||
// only get encryption module if we found one in the header | |||
if (!empty($encryptionModuleId)) { | |||
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); | |||
$shouldEncrypt = true; | |||
} | |||
} | |||
} catch (ModuleDoesNotExistsException $e) { | |||
$this->logger->warning('Encryption module "' . $encryptionModuleId . | |||
'" not found, file will be stored unencrypted'); | |||
} | |||
if($shouldEncrypt === true && !$this->util->isExcluded($path) && $encryptionModule !== null) { | |||
$source = $this->storage->fopen($path, $mode); | |||
$handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header, | |||
$this->uid, $encryptionModule, $this->storage, $this, $this->util, $mode, | |||
$size, $unencryptedSize); | |||
return $handle; | |||
} else { | |||
return $this->storage->fopen($path, $mode); | |||
} | |||
} | |||
/** | |||
* return full path, including mount point | |||
* | |||
* @param string $path relative to mount point | |||
* @return string full path including mount point | |||
*/ | |||
protected function getFullPath($path) { | |||
return \OC\Files\Filesystem::normalizePath($this->mountPoint . '/' . $path); | |||
} | |||
/** | |||
* read header from file | |||
* | |||
* @param string $path | |||
* @return array | |||
*/ | |||
protected function getHeader($path) { | |||
$header = ''; | |||
if ($this->storage->file_exists($path)) { | |||
$handle = $this->storage->fopen($path, 'r'); | |||
$header = fread($handle, $this->util->getHeaderSize()); | |||
fclose($handle); | |||
} | |||
return $this->util->readHeader($header); | |||
} | |||
/** | |||
* read encryption module needed to read/write the file located at $path | |||
* | |||
* @param string $path | |||
* @return \OCP\Encryption\IEncryptionModule|null | |||
*/ | |||
protected function getEncryptionModule($path) { | |||
$encryptionModule = null; | |||
$header = $this->getHeader($path); | |||
$encryptionModuleId = $this->util->getEncryptionModuleId($header); | |||
if (!empty($encryptionModuleId)) { | |||
try { | |||
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); | |||
} catch (ModuleDoesNotExistsException $e) { | |||
$this->logger->critical('Encryption module defined in "' . $path . '" mot loaded!'); | |||
throw $e; | |||
} | |||
} | |||
return $encryptionModule; | |||
} | |||
public function updateUnencryptedSize($path, $unencryptedSize) { | |||
$this->unencryptedSize[$path] = $unencryptedSize; | |||
} | |||
} |
@@ -0,0 +1,385 @@ | |||
<?php | |||
/** | |||
* ownCloud - Encryption stream wrapper | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OC\Files\Stream; | |||
use Icewind\Streams\Wrapper; | |||
use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; | |||
class Encryption extends Wrapper { | |||
/** @var \OC\Encryption\Util */ | |||
protected $util; | |||
/** @var \OCP\Encryption\IEncryptionModule */ | |||
protected $encryptionModule; | |||
/** @var \OC\Files\Storage\Storage */ | |||
protected $storage; | |||
/** @var \OC\Files\Storage\Wrapper\Encryption */ | |||
protected $encryptionStorage; | |||
/** @var string */ | |||
protected $internalPath; | |||
/** @var integer */ | |||
protected $size; | |||
/** @var integer */ | |||
protected $position; | |||
/** @var integer */ | |||
protected $unencryptedSize; | |||
/** @var integer */ | |||
protected $unencryptedBlockSize; | |||
/** @var array */ | |||
protected $header; | |||
/** @var string */ | |||
protected $fullPath; | |||
/** | |||
* header data returned by the encryption module, will be written to the file | |||
* in case of a write operation | |||
* | |||
* @var array | |||
*/ | |||
protected $newHeader; | |||
/** | |||
* user who perform the read/write operation null for public access | |||
* | |||
* @var string | |||
*/ | |||
protected $uid; | |||
/** @var bool */ | |||
protected $readOnly; | |||
/** @var array */ | |||
protected $expectedContextProperties; | |||
public function __construct() { | |||
$this->expectedContextProperties = array( | |||
'source', | |||
'storage', | |||
'internalPath', | |||
'fullPath', | |||
'encryptionModule', | |||
'header', | |||
'uid', | |||
'util', | |||
'size', | |||
'unencryptedSize', | |||
'encryptionStorage' | |||
); | |||
} | |||
/** | |||
* Wraps a stream with the provided callbacks | |||
* | |||
* @param resource $source | |||
* @param string $internalPath relative to mount point | |||
* @param string $fullPath relative to data/ | |||
* @param array $header | |||
* @param sting $uid | |||
* @param \OCP\Encryption\IEncryptionModule $encryptionModule | |||
* @param \OC\Files\Storage\Storage $storage | |||
* @param OC\Files\Storage\Wrapper\Encryption $encStorage | |||
* @param \OC\Encryption\Util $util | |||
* @param string $mode | |||
* @param int $size | |||
* @param int $unencryptedSize | |||
* @return resource | |||
* | |||
* @throws \BadMethodCallException | |||
*/ | |||
public static function wrap($source, $internalPath, $fullPath, array $header, | |||
$uid, \OCP\Encryption\IEncryptionModule $encryptionModule, | |||
\OC\Files\Storage\Storage $storage, \OC\Files\Storage\Wrapper\Encryption $encStorage, | |||
\OC\Encryption\Util $util, $mode, $size, $unencryptedSize) { | |||
$context = stream_context_create(array( | |||
'ocencryption' => array( | |||
'source' => $source, | |||
'storage' => $storage, | |||
'internalPath' => $internalPath, | |||
'fullPath' => $fullPath, | |||
'encryptionModule' => $encryptionModule, | |||
'header' => $header, | |||
'uid' => $uid, | |||
'util' => $util, | |||
'size' => $size, | |||
'unencryptedSize' => $unencryptedSize, | |||
'encryptionStorage' => $encStorage | |||
) | |||
)); | |||
return self::wrapSource($source, $mode, $context, 'ocencryption', 'OC\Files\Stream\Encryption'); | |||
} | |||
/** | |||
* add stream wrapper | |||
* | |||
* @param resource $source | |||
* @param string $mode | |||
* @param array $context | |||
* @param string $protocol | |||
* @param string $class | |||
* @return resource | |||
* @throws \BadMethodCallException | |||
*/ | |||
protected static function wrapSource($source, $mode, $context, $protocol, $class) { | |||
try { | |||
stream_wrapper_register($protocol, $class); | |||
if (@rewinddir($source) === false) { | |||
$wrapped = fopen($protocol . '://', $mode, false, $context); | |||
} else { | |||
$wrapped = opendir($protocol . '://', $context); | |||
} | |||
} catch (\BadMethodCallException $e) { | |||
stream_wrapper_unregister($protocol); | |||
throw $e; | |||
} | |||
stream_wrapper_unregister($protocol); | |||
return $wrapped; | |||
} | |||
/** | |||
* Load the source from the stream context and return the context options | |||
* | |||
* @param string $name | |||
* @return array | |||
* @throws \BadMethodCallException | |||
*/ | |||
protected function loadContext($name) { | |||
$context = parent::loadContext($name); | |||
foreach ($this->expectedContextProperties as $property) { | |||
if (isset($context[$property])) { | |||
$this->{$property} = $context[$property]; | |||
} else { | |||
throw new \BadMethodCallException('Invalid context, "' . $property . '" options not set'); | |||
} | |||
} | |||
return $context; | |||
} | |||
public function stream_open($path, $mode, $options, &$opened_path) { | |||
$this->loadContext('ocencryption'); | |||
$this->position = 0; | |||
$this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize(); | |||
if ( | |||
$mode === 'w' | |||
|| $mode === 'w+' | |||
|| $mode === 'wb' | |||
|| $mode === 'wb+' | |||
) { | |||
// We're writing a new file so start write counter with 0 bytes | |||
// TODO can we remove this completely? | |||
//$this->unencryptedSize = 0; | |||
//$this->size = 0; | |||
$this->readOnly = false; | |||
} else { | |||
$this->readOnly = true; | |||
} | |||
$sharePath = $this->fullPath; | |||
if (!$this->storage->file_exists($this->internalPath)) { | |||
$sharePath = dirname($path); | |||
} | |||
$accessList = $this->util->getSharingUsersArray($sharePath); | |||
$this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $this->header, $accessList); | |||
return true; | |||
} | |||
public function stream_read($count) { | |||
$result = ''; | |||
// skip the header if we read the file from the beginning | |||
if ($this->position === 0 && !empty($this->header)) { | |||
parent::stream_read($this->util->getBlockSize()); | |||
} | |||
while ($count > 0) { | |||
$remainingLength = $count; | |||
// update the cache of the current block | |||
$data = parent::stream_read($this->util->getBlockSize()); | |||
$decrypted = $this->encryptionModule->decrypt($data); | |||
// determine the relative position in the current block | |||
$blockPosition = ($this->position % $this->unencryptedBlockSize); | |||
// if entire read inside current block then only position needs to be updated | |||
if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { | |||
$result .= substr($decrypted, $blockPosition, $remainingLength); | |||
$this->position += $remainingLength; | |||
$count = 0; | |||
// otherwise remainder of current block is fetched, the block is flushed and the position updated | |||
} else { | |||
$result .= substr($decrypted, $blockPosition); | |||
$this->position += ($this->unencryptedBlockSize - $blockPosition); | |||
$count -= ($this->unencryptedBlockSize - $blockPosition); | |||
} | |||
} | |||
return $result; | |||
} | |||
public function stream_write($data) { | |||
if ($this->position === 0) { | |||
$this->writeHeader(); | |||
} | |||
$length = 0; | |||
// loop over $data to fit it in 6126 sized unencrypted blocks | |||
while (strlen($data) > 0) { | |||
$remainingLength = strlen($data); | |||
// read current block | |||
$currentBlock = parent::stream_read($this->util->getBlockSize()); | |||
$decrypted = $this->encryptionModule->decrypt($currentBlock, $this->uid); | |||
// for seekable streams the pointer is moved back to the beginning of the encrypted block | |||
// flush will start writing there when the position moves to another block | |||
$positionInFile = floor($this->position / $this->unencryptedBlockSize) * | |||
$this->util->getBlockSize() + $this->util->getHeaderSize(); | |||
$resultFseek = parent::stream_seek($positionInFile); | |||
// only allow writes on seekable streams, or at the end of the encrypted stream | |||
if ($resultFseek || $positionInFile === $this->size) { | |||
// determine the relative position in the current block | |||
$blockPosition = ($this->position % $this->unencryptedBlockSize); | |||
// check if $data fits in current block | |||
// if so, overwrite existing data (if any) | |||
// update position and liberate $data | |||
if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { | |||
$decrypted = substr($decrypted, 0, $blockPosition) | |||
. $data . substr($decrypted, $blockPosition + $remainingLength); | |||
$encrypted = $this->encryptionModule->encrypt($decrypted); | |||
parent::stream_write($encrypted); | |||
$this->position += $remainingLength; | |||
$length += $remainingLength; | |||
$data = ''; | |||
// if $data doens't fit the current block, the fill the current block and reiterate | |||
// after the block is filled, it is flushed and $data is updatedxxx | |||
} else { | |||
$decrypted = substr($decrypted, 0, $blockPosition) . | |||
substr($data, 0, $this->unencryptedBlockSize - $blockPosition); | |||
$encrypted = $this->encryptionModule->encrypt($decrypted); | |||
parent::stream_write($encrypted); | |||
$this->position += ($this->unencryptedBlockSize - $blockPosition); | |||
$this->size = max($this->size, $this->stream_tell()); | |||
$length += ($this->unencryptedBlockSize - $blockPosition); | |||
$data = substr($data, $this->unencryptedBlockSize - $blockPosition); | |||
} | |||
} else { | |||
$encrypted = $this->encryptionModule->encrypt($data); | |||
parent::stream_write($encrypted); | |||
$data = ''; | |||
} | |||
} | |||
$this->unencryptedSize = max($this->unencryptedSize, $this->position); | |||
return $length; | |||
} | |||
public function stream_tell() { | |||
return $this->position; | |||
} | |||
public function stream_seek($offset, $whence = SEEK_SET) { | |||
$return = false; | |||
switch ($whence) { | |||
case SEEK_SET: | |||
if ($offset < $this->unencryptedSize && $offset >= 0) { | |||
$newPosition = $offset; | |||
} | |||
break; | |||
case SEEK_CUR: | |||
if ($offset >= 0) { | |||
$newPosition = $offset + $this->position; | |||
} | |||
break; | |||
case SEEK_END: | |||
if ($this->unencryptedSize + $offset >= 0) { | |||
$newPosition = $this->unencryptedSize + $offset; | |||
} | |||
break; | |||
default: | |||
return $return; | |||
} | |||
$newFilePosition = floor($newPosition / $this->unencryptedBlockSize) | |||
* $this->util->getBlockSize() + $this->util->getHeaderSize(); | |||
if (parent::stream_seek($newFilePosition)) { | |||
$this->position = $newPosition; | |||
$return = true; | |||
} | |||
return $return; | |||
} | |||
public function stream_close() { | |||
$this->flush(); | |||
return parent::stream_close(); | |||
} | |||
/** | |||
* tell encryption module that we are done and write remaining data to the file | |||
*/ | |||
protected function flush() { | |||
$remainingData = $this->encryptionModule->end($this->fullPath); | |||
if ($this->readOnly === false) { | |||
if(!empty($remainingData)) { | |||
parent::stream_write($remainingData); | |||
} | |||
$this->encryptionStorage->updateUnencryptedSize($this->fullPath, $this->unencryptedSize); | |||
} | |||
} | |||
/** | |||
* write header at beginning of encrypted file | |||
* | |||
* @throws EncryptionHeaderKeyExistsException if header key is already in use | |||
*/ | |||
private function writeHeader() { | |||
$header = $this->util->createHeader($this->newHeader, $this->encryptionModule); | |||
parent::stream_write($header); | |||
} | |||
} |
@@ -45,9 +45,18 @@ class Server extends SimpleContainer implements IServerContainer { | |||
$this->registerService('ContactsManager', function ($c) { | |||
return new ContactsManager(); | |||
}); | |||
$this->registerService('PreviewManager', function (Server $c) { | |||
return new PreviewManager($c->getConfig()); | |||
}); | |||
$this->registerService('EncryptionManager', function (Server $c) { | |||
return new Encryption\Manager($c->getConfig()); | |||
}); | |||
$this->registerService('EncryptionKeyStorageFactory', function ($c) { | |||
return new Encryption\Keys\Factory(); | |||
}); | |||
$this->registerService('TagMapper', function(Server $c) { | |||
return new TagMapper($c->getDatabaseConnection()); | |||
}); | |||
@@ -347,6 +356,24 @@ class Server extends SimpleContainer implements IServerContainer { | |||
return $this->query('ContactsManager'); | |||
} | |||
/** | |||
* @return \OC\Encryption\Manager | |||
*/ | |||
function getEncryptionManager() { | |||
return $this->query('EncryptionManager'); | |||
} | |||
/** | |||
* @param string $encryptionModuleId encryption module ID | |||
* | |||
* @return \OCP\Encryption\Keys\IStorage | |||
*/ | |||
function getEncryptionKeyStorage($encryptionModuleId) { | |||
$view = new \OC\Files\View(); | |||
$util = new \OC\Encryption\Util($view, \OC::$server->getUserManager()); | |||
return $this->query('EncryptionKeyStorageFactory')->get($encryptionModuleId, $view, $util); | |||
} | |||
/** | |||
* The current request object holding all information about the request | |||
* currently being processed is returned from this method. |
@@ -0,0 +1,115 @@ | |||
<?php | |||
/** | |||
* ownCloud - public interface of ownCloud for encryption modules | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OCP\Encryption; | |||
interface IEncryptionModule { | |||
/** | |||
* @return string defining the technical unique id | |||
*/ | |||
public function getId(); | |||
/** | |||
* In comparison to getKey() this function returns a human readable (maybe translated) name | |||
* | |||
* @return string | |||
*/ | |||
public function getDisplayName(); | |||
/** | |||
* start receiving chunks from a file. This is the place where you can | |||
* perform some initial step before starting encrypting/decrypting the | |||
* chunks | |||
* | |||
* @param string $path to the file | |||
* @param string $user who read/write the file (null for public access) | |||
* @param array $header contains the header data read from the file | |||
* @param array $accessList who has access to the file contains the key 'users' and 'public' | |||
* | |||
* $return array $header contain data as key-value pairs which should be | |||
* written to the header, in case of a write operation | |||
* or if no additional data is needed return a empty array | |||
*/ | |||
public function begin($path, $user, $header, $accessList); | |||
/** | |||
* last chunk received. This is the place where you can perform some final | |||
* operation and return some remaining data if something is left in your | |||
* buffer. | |||
* | |||
* @param string $path to the file | |||
* @return string remained data which should be written to the file in case | |||
* of a write operation | |||
*/ | |||
public function end($path); | |||
/** | |||
* encrypt data | |||
* | |||
* @param string $data you want to encrypt | |||
* @return mixed encrypted data | |||
*/ | |||
public function encrypt($data); | |||
/** | |||
* decrypt data | |||
* | |||
* @param string $data you want to decrypt | |||
* @return mixed decrypted data | |||
*/ | |||
public function decrypt($data); | |||
/** | |||
* update encrypted file, e.g. give additional users access to the file | |||
* | |||
* @param string $path path to the file which should be updated | |||
* @param array $accessList who has access to the file contains the key 'users' and 'public' | |||
* @return boolean | |||
*/ | |||
public function update($path, $accessList); | |||
/** | |||
* should the file be encrypted or not | |||
* | |||
* @param string $path | |||
* @return boolean | |||
*/ | |||
public function shouldEncrypt($path); | |||
/** | |||
* calculate unencrypted size | |||
* | |||
* @param string $path to file | |||
* @return integer unencrypted size | |||
*/ | |||
public function calculateUnencryptedSize($path); | |||
/** | |||
* get size of the unencrypted payload per block. | |||
* ownCloud read/write files with a block size of 8192 byte | |||
* | |||
* @return integer | |||
*/ | |||
public function getUnencryptedBlockSize(); | |||
} |
@@ -0,0 +1,92 @@ | |||
<?php | |||
/** | |||
* ownCloud - manage encryption modules | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OCP\Encryption; | |||
// | |||
// TODO: move exceptions to OCP | |||
// | |||
use OC\Encryption\Exceptions\ModuleDoesNotExistsException; | |||
use OC\Encryption\Exceptions\ModuleAlreadyExistsException; | |||
/** | |||
* This class provides access to files encryption apps. | |||
* | |||
*/ | |||
interface IManager { | |||
/** | |||
* Check if encryption is available (at least one encryption module needs to be enabled) | |||
* | |||
* @return bool true if enabled, false if not | |||
*/ | |||
function isEnabled(); | |||
/** | |||
* Registers an encryption module | |||
* | |||
* @param IEncryptionModule $module | |||
* @throws ModuleAlreadyExistsException | |||
*/ | |||
function registerEncryptionModule(IEncryptionModule $module); | |||
/** | |||
* Unregisters an encryption module | |||
* | |||
* @param IEncryptionModule $module | |||
*/ | |||
function unregisterEncryptionModule(IEncryptionModule $module); | |||
/** | |||
* get a list of all encryption modules | |||
* | |||
* @return array | |||
*/ | |||
function getEncryptionModules(); | |||
/** | |||
* get a specific encryption module | |||
* | |||
* @param string $moduleId | |||
* @return IEncryptionModule | |||
* @throws ModuleDoesNotExistsException | |||
*/ | |||
function getEncryptionModule($moduleId); | |||
/** | |||
* get default encryption module | |||
* | |||
* @return \OCP\Encryption\IEncryptionModule | |||
* @throws Exceptions\ModuleDoesNotExistsException | |||
*/ | |||
public function getDefaultEncryptionModule(); | |||
/** | |||
* set default encryption module Id | |||
* | |||
* @param string $moduleId | |||
* @return string | |||
*/ | |||
public function setDefaultEncryptionModule($moduleId); | |||
} |
@@ -0,0 +1,117 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace OCP\Encryption\Keys; | |||
interface IStorage { | |||
/** | |||
* get user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want the key | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getUserKey($uid, $keyId); | |||
/** | |||
* get file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getFileKey($path, $keyId); | |||
/** | |||
* get system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function getSystemUserKey($keyId); | |||
/** | |||
* set user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want the key | |||
* @param string $keyId id of the key | |||
* @param mixed $key | |||
*/ | |||
public function setUserKey($uid, $keyId, $key); | |||
/** | |||
* set file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* @param boolean | |||
*/ | |||
public function setFileKey($path, $keyId, $key); | |||
/** | |||
* set system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* @param mixed $key | |||
* | |||
* @return mixed key | |||
*/ | |||
public function setSystemUserKey($keyId, $key); | |||
/** | |||
* delete user specific key | |||
* | |||
* @param string $uid ID if the user for whom we want to delete the key | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteUserKey($uid, $keyId); | |||
/** | |||
* delete file specific key | |||
* | |||
* @param string $path path to file | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteFileKey($path, $keyId); | |||
/** | |||
* delete system-wide encryption keys not related to a specific user, | |||
* e.g something like a key for public link shares | |||
* | |||
* @param string $keyId id of the key | |||
* | |||
* @return boolean | |||
*/ | |||
public function deleteSystemUserKey($keyId); | |||
} |
@@ -57,6 +57,23 @@ $template->assign('shareExcludeGroups', $excludeGroups); | |||
$excludedGroupsList = $appConfig->getValue('core', 'shareapi_exclude_groups_list', ''); | |||
$excludedGroupsList = explode(',', $excludedGroupsList); // FIXME: this should be JSON! | |||
$template->assign('shareExcludedGroupsList', implode('|', $excludedGroupsList)); | |||
$template->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); | |||
$encryptionModules = \OC::$server->getEncryptionManager()->getEncryptionModules(); | |||
try { | |||
$defaultEncryptionModule = \OC::$server->getEncryptionManager()->getDefaultEncryptionModule(); | |||
$defaultEncryptionModuleId = $defaultEncryptionModule->getId(); | |||
} catch (Exception $e) { | |||
$defaultEncryptionModule = null; | |||
} | |||
$encModulues = array(); | |||
foreach ($encryptionModules as $module) { | |||
$encModulues[$module->getId()]['displayName'] = $module->getDisplayName(); | |||
$encModulues[$module->getId()]['default'] = false; | |||
if ($defaultEncryptionModule && $module->getId() === $defaultEncryptionModuleId) { | |||
$encModulues[$module->getId()]['default'] = true; | |||
} | |||
} | |||
$template->assign('encryptionModules', $encModulues); | |||
// If the current web root is non-empty but the web root from the config is, | |||
// and system cron is used, the URL generator fails to build valid URLs. | |||
@@ -118,6 +135,7 @@ $formsAndMore = array_merge($formsAndMore, $formsMap); | |||
// add bottom hardcoded forms from the template | |||
$formsAndMore[] = array('anchor' => 'backgroundjobs', 'section-name' => $l->t('Cron')); | |||
$formsAndMore[] = array('anchor' => 'shareAPI', 'section-name' => $l->t('Sharing')); | |||
$formsAndMore[] = array('anchor' => 'encryptionAPI', 'section-name' => $l->t('Server Side Encryption')); | |||
$formsAndMore[] = array('anchor' => 'mail_general_settings', 'section-name' => $l->t('Email Server')); | |||
$formsAndMore[] = array('anchor' => 'log-section', 'section-name' => $l->t('Log')); | |||
@@ -386,3 +386,12 @@ doesnotexist:-o-prefocus, .strengthify-wrapper { | |||
font-weight: normal; | |||
margin-top: 5px; | |||
} | |||
#selectEncryptionModules { | |||
margin-left: 30px; | |||
padding: 10px; | |||
} | |||
#encryptionModules { | |||
padding: 10px; | |||
} |
@@ -54,6 +54,22 @@ $(document).ready(function(){ | |||
$('#shareAPI p:not(#enable)').toggleClass('hidden', !this.checked); | |||
}); | |||
$('#encryptionEnabled').change(function() { | |||
$('#encryptionAPI div#selectEncryptionModules').toggleClass('hidden'); | |||
}); | |||
$('#encryptionAPI input').change(function() { | |||
var value = $(this).val(); | |||
if ($(this).attr('type') === 'checkbox') { | |||
if (this.checked) { | |||
value = 'yes'; | |||
} else { | |||
value = 'no'; | |||
} | |||
} | |||
OC.AppConfig.setValue('core', $(this).attr('name'), value); | |||
}); | |||
$('#shareAPI input:not(#excludedGroups)').change(function() { | |||
var value = $(this).val(); | |||
if ($(this).attr('type') === 'checkbox') { |
@@ -356,6 +356,30 @@ if ($_['cronErrors']) { | |||
</p> | |||
</div> | |||
<div class="section" id='encryptionAPI'> | |||
<h2><?php p($l->t('Server Side Encryption'));?></h2> | |||
<p id="enable"> | |||
<input type="checkbox" name="encryption_enabled" id="encryptionEnabled" | |||
value="1" <?php if ($_['encryptionEnabled']) print_unescaped('checked="checked"'); ?> /> | |||
<label for="encryptionEnabled"><?php p($l->t('Enable Server-Side-Encryption'));?></label><br/> | |||
</p> | |||
<div id='selectEncryptionModules' class="<?php if (!$_['encryptionEnabled']) { p('hidden'); }?>"> | |||
<?php if (empty($_['encryptionModules'])): p('No encryption module loaded, please load a encryption module in the app menu'); | |||
else: ?> | |||
<h3>Select default encryption module:</h3> | |||
<fieldset id='encryptionModules'> | |||
<?php foreach ($_['encryptionModules'] as $id => $module): ?> | |||
<input type="radio" id="<?php p($id) ?>" | |||
name="default_encryption_module" | |||
value="<?php p($id) ?>" | |||
<?php if($module['default']) { p('checked'); } ?>> | |||
<label for="<?php p($id) ?>"><?php p($module['displayName']) ?></label><br /> | |||
<?php endforeach;?> | |||
</fieldset> | |||
<?php endif; ?> | |||
</div> | |||
</div> | |||
<div class="section"> | |||
<form id="mail_general_settings" class="mail_settings"> | |||
<h2><?php p($l->t('Email Server'));?></h2> |
@@ -0,0 +1,280 @@ | |||
<?php | |||
/** | |||
* ownCloud | |||
* | |||
* @copyright (C) 2015 ownCloud, Inc. | |||
* | |||
* @author Bjoern Schiessle <schiessle@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
namespace Test\Encryption\Keys; | |||
use OC\Encryption\Keys\Storage; | |||
use Test\TestCase; | |||
class StorageTest extends TestCase { | |||
/** @var \PHPUnit_Framework_MockObject_MockObject */ | |||
protected $util; | |||
/** @var \PHPUnit_Framework_MockObject_MockObject */ | |||
protected $view; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->util = $this->getMockBuilder('OC\Encryption\Util') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$this->view = $this->getMockBuilder('OC\Files\View') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
} | |||
public function testSetFileKey() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(false); | |||
$this->view->expects($this->once()) | |||
->method('file_put_contents') | |||
->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'), | |||
$this->equalTo('key')) | |||
->willReturn(strlen('key')); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key') | |||
); | |||
} | |||
public function testGetFileKey() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(false); | |||
$this->view->expects($this->once()) | |||
->method('file_get_contents') | |||
->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn('key'); | |||
$this->view->expects($this->once()) | |||
->method('file_exists') | |||
->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertSame('key', | |||
$storage->getFileKey('user1/files/foo.txt', 'fileKey') | |||
); | |||
} | |||
public function testSetFileKeySystemWide() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(true); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->view->expects($this->once()) | |||
->method('file_put_contents') | |||
->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'), | |||
$this->equalTo('key')) | |||
->willReturn(strlen('key')); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key') | |||
); | |||
} | |||
public function testGetFileKeySystemWide() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(true); | |||
$this->view->expects($this->once()) | |||
->method('file_get_contents') | |||
->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn('key'); | |||
$this->view->expects($this->once()) | |||
->method('file_exists') | |||
->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertSame('key', | |||
$storage->getFileKey('user1/files/foo.txt', 'fileKey') | |||
); | |||
} | |||
public function testSetSystemUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('file_put_contents') | |||
->with($this->equalTo('/files_encryption/encModule/shareKey_56884'), | |||
$this->equalTo('key')) | |||
->willReturn(strlen('key')); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->setSystemUserKey('shareKey_56884', 'key') | |||
); | |||
} | |||
public function testSetUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('file_put_contents') | |||
->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'), | |||
$this->equalTo('key')) | |||
->willReturn(strlen('key')); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->setUserKey('user1', 'publicKey', 'key') | |||
); | |||
} | |||
public function testGetSystemUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('file_get_contents') | |||
->with($this->equalTo('/files_encryption/encModule/shareKey_56884')) | |||
->willReturn('key'); | |||
$this->view->expects($this->once()) | |||
->method('file_exists') | |||
->with($this->equalTo('/files_encryption/encModule/shareKey_56884')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertSame('key', | |||
$storage->getSystemUserKey('shareKey_56884') | |||
); | |||
} | |||
public function testGetUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('file_get_contents') | |||
->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey')) | |||
->willReturn('key'); | |||
$this->view->expects($this->once()) | |||
->method('file_exists') | |||
->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertSame('key', | |||
$storage->getUserKey('user1', 'publicKey') | |||
); | |||
} | |||
public function testDeleteUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('unlink') | |||
->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->deleteUserKey('user1', 'publicKey') | |||
); | |||
} | |||
public function testDeleteSystemUserKey() { | |||
$this->view->expects($this->once()) | |||
->method('unlink') | |||
->with($this->equalTo('/files_encryption/encModule/shareKey_56884')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->deleteSystemUserKey('shareKey_56884') | |||
); | |||
} | |||
public function testDeleteFileKeySystemWide() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(true); | |||
$this->view->expects($this->once()) | |||
->method('unlink') | |||
->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->deleteFileKey('user1/files/foo.txt', 'fileKey') | |||
); | |||
} | |||
public function testDeleteFileKey() { | |||
$this->util->expects($this->any()) | |||
->method('getUidAndFilename') | |||
->willReturn(array('user1', '/files/foo.txt')); | |||
$this->util->expects($this->any()) | |||
->method('stripPartialFileExtension') | |||
->willReturnArgument(0); | |||
$this->util->expects($this->any()) | |||
->method('isSystemWideMountPoint') | |||
->willReturn(false); | |||
$this->view->expects($this->once()) | |||
->method('unlink') | |||
->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey')) | |||
->willReturn(true); | |||
$storage = new Storage('encModule', $this->view, $this->util); | |||
$this->assertTrue( | |||
$storage->deleteFileKey('user1/files/foo.txt', 'fileKey') | |||
); | |||
} | |||
} |
@@ -0,0 +1,114 @@ | |||
<?php | |||
namespace Test\Encryption; | |||
use OC\Encryption\Manager; | |||
use Test\TestCase; | |||
class ManagerTest extends TestCase { | |||
public function testManagerIsDisabled() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$m = new Manager($config); | |||
$this->assertFalse($m->isEnabled()); | |||
} | |||
public function testManagerIsDisabledIfEnabledButNoModules() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(true); | |||
$m = new Manager($config); | |||
$this->assertFalse($m->isEnabled()); | |||
} | |||
public function testManagerIsDisabledIfDisabledButModules() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(false); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertFalse($m->isEnabled()); | |||
} | |||
public function testManagerIsEnabled() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getSystemValue')->willReturn(true); | |||
$config->expects($this->any())->method('getAppValue')->willReturn('yes'); | |||
$m = new Manager($config); | |||
$this->assertTrue($m->isEnabled()); | |||
} | |||
/** | |||
* @expectedException \OC\Encryption\Exceptions\ModuleAlreadyExistsException | |||
* @expectedExceptionMessage Id "0" already used by encryption module "TestDummyModule0" | |||
*/ | |||
public function testModuleRegistration() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn('yes'); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertSame(1, count($m->getEncryptionModules())); | |||
$m->registerEncryptionModule($em); | |||
} | |||
public function testModuleUnRegistration() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(true); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertSame(1, | |||
count($m->getEncryptionModules()) | |||
); | |||
$m->unregisterEncryptionModule($em); | |||
$this->assertEmpty($m->getEncryptionModules()); | |||
} | |||
/** | |||
* @expectedException \OC\Encryption\Exceptions\ModuleDoesNotExistsException | |||
* @expectedExceptionMessage Module with id: unknown does not exists. | |||
*/ | |||
public function testGetEncryptionModuleUnknown() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(true); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertSame(1, count($m->getEncryptionModules())); | |||
$m->getEncryptionModule('unknown'); | |||
} | |||
public function testGetEncryptionModule() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(true); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertSame(1, count($m->getEncryptionModules())); | |||
$en0 = $m->getEncryptionModule(0); | |||
$this->assertEquals(0, $en0->getId()); | |||
} | |||
public function testGetDefaultEncryptionModule() { | |||
$config = $this->getMock('\OCP\IConfig'); | |||
$config->expects($this->any())->method('getAppValue')->willReturn(true); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn(0); | |||
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); | |||
$m = new Manager($config); | |||
$m->registerEncryptionModule($em); | |||
$this->assertSame(1, count($m->getEncryptionModules())); | |||
$en0 = $m->getEncryptionModule(0); | |||
$this->assertEquals(0, $en0->getId()); | |||
} | |||
} |
@@ -0,0 +1,101 @@ | |||
<?php | |||
namespace Test\Encryption; | |||
use OC\Encryption\Util; | |||
use Test\TestCase; | |||
class UtilTest extends TestCase { | |||
/** | |||
* block size will always be 8192 for a PHP stream | |||
* @see https://bugs.php.net/bug.php?id=21641 | |||
* @var integer | |||
*/ | |||
protected $headerSize = 8192; | |||
/** @var \PHPUnit_Framework_MockObject_MockObject */ | |||
protected $view; | |||
/** @var \PHPUnit_Framework_MockObject_MockObject */ | |||
protected $userManager; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->view = $this->getMockBuilder('OC\Files\View') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$this->userManager = $this->getMockBuilder('OC\User\Manager') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
} | |||
/** | |||
* @dataProvider providesHeadersForEncryptionModule | |||
*/ | |||
public function testGetEncryptionModuleId($expected, $header) { | |||
$u = new Util($this->view, $this->userManager); | |||
$id = $u->getEncryptionModuleId($header); | |||
$this->assertEquals($expected, $id); | |||
} | |||
public function providesHeadersForEncryptionModule() { | |||
return [ | |||
['', []], | |||
['', ['1']], | |||
[2, ['oc_encryption_module' => 2]], | |||
]; | |||
} | |||
/** | |||
* @dataProvider providesHeaders | |||
*/ | |||
public function testReadHeader($header, $expected, $moduleId) { | |||
$expected['oc_encryption_module'] = $moduleId; | |||
$u = new Util($this->view, $this->userManager); | |||
$result = $u->readHeader($header); | |||
$this->assertSameSize($expected, $result); | |||
foreach ($expected as $key => $value) { | |||
$this->assertArrayHasKey($key, $result); | |||
$this->assertSame($value, $result[$key]); | |||
} | |||
} | |||
/** | |||
* @dataProvider providesHeaders | |||
*/ | |||
public function testCreateHeader($expected, $header, $moduleId) { | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn($moduleId); | |||
$u = new Util($this->view, $this->userManager); | |||
$result = $u->createHeader($header, $em); | |||
$this->assertEquals($expected, $result); | |||
} | |||
public function providesHeaders() { | |||
return [ | |||
[str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) | |||
, [], '0'], | |||
[str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT) | |||
, ['custom_header' => 'foo'], '0'], | |||
]; | |||
} | |||
/** | |||
* @expectedException \OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException | |||
*/ | |||
public function testCreateHeaderFailed() { | |||
$header = array('header1' => 1, 'header2' => 2, 'oc_encryption_module' => 'foo'); | |||
$em = $this->getMock('\OCP\Encryption\IEncryptionModule'); | |||
$em->expects($this->any())->method('getId')->willReturn('moduleId'); | |||
$u = new Util($this->view, $this->userManager); | |||
$u->createHeader($header, $em); | |||
} | |||
} |