Merge branch 'files_encryption' of github.com:owncloud/core into files_encryption

This commit is contained in:
Björn Schießle 2013-05-17 11:20:17 +02:00
commit 0b40c1d08a
7 changed files with 302 additions and 95 deletions

View File

@ -21,74 +21,14 @@ $recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] == 1){
$view = new \OC\Files\View('/');
if ($recoveryKeyId === null) {
$recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8);
\OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId);
}
if (!$view->is_dir('/owncloud_private_key')) {
$view->mkdir('/owncloud_private_key');
}
if (
(!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key")
|| !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key"))
) {
$keypair = \OCA\Encryption\Crypt::createKeypair();
\OC_FileProxy::$enabled = false;
// Save public key
if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
$view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
// Encrypt private key empthy passphrase
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $_POST['recoveryPassword']);
// Save private key
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
// create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']);
if (!$view->is_dir('/control-file')) {
$view->mkdir('/control-file');
}
$view->file_put_contents('/control-file/controlfile.enc', $encryptedControlData);
\OC_FileProxy::$enabled = true;
// Set recoveryAdmin as enabled
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
$return = true;
} else { // get recovery key and check the password
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword($_POST['recoveryPassword']);
if ($return) {
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
}
}
$return = \Helper::adminEnableRecovery($recoveryKeyId, $_POST['recoveryPassword']);
// Disable recoveryAdmin
} elseif (
isset($_POST['adminEnableRecovery'])
&& 0 == $_POST['adminEnableRecovery']
) {
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword($_POST['recoveryPassword']);
if ($return) {
// Set recoveryAdmin as disabled
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0);
}
$return = \Helper::adminDisableRecovery($_POST['recoveryPassword']);
}
// Return success or failure

View File

@ -26,6 +26,10 @@ namespace OCA\Encryption;
/**
* @brief Class to manage registration of hooks an various helper methods
*/
/**
* Class Helper
* @package OCA\Encryption
*/
class Helper {
/**
@ -89,4 +93,92 @@ class Helper {
return true;
}
/**
* @brief enable recovery
*
* @param $recoveryKeyId
* @param $recoveryPassword
* @internal param \OCA\Encryption\Util $util
* @internal param string $password
* @return bool
*/
public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) {
$view = new \OC\Files\View('/');
if ($recoveryKeyId === null) {
$recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8);
\OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId);
}
if (!$view->is_dir('/owncloud_private_key')) {
$view->mkdir('/owncloud_private_key');
}
if (
(!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key")
|| !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key"))
) {
$keypair = \OCA\Encryption\Crypt::createKeypair();
\OC_FileProxy::$enabled = false;
// Save public key
if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
$view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
// Encrypt private key empthy passphrase
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword);
// Save private key
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
// create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']);
if (!$view->is_dir('/control-file')) {
$view->mkdir('/control-file');
}
$view->file_put_contents('/control-file/controlfile.enc', $encryptedControlData);
\OC_FileProxy::$enabled = true;
// Set recoveryAdmin as enabled
\OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
$return = true;
} else { // get recovery key and check the password
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword($_POST['recoveryPassword']);
if ($return) {
\OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
}
}
return $return;
}
/**
* @brief disable recovery
*
* @param $recoveryPassword
* @return bool
*/
public static function adminDisableRecovery($recoveryPassword) {
$util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword($recoveryPassword);
if ($return) {
// Set recoveryAdmin as disabled
\OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0);
}
return $return;
}
}

View File

@ -1328,7 +1328,7 @@ class Util {
foreach ($dirContent as $item) {
$filePath = substr($item['path'], 25);
if ($item['type'] == 'dir') {
$this->addRecoveryKey($filePath.'/');
$this->addRecoveryKeys($filePath.'/');
} else {
$session = new Session(new \OC_FilesystemView('/'));
$sharingEnabled = \OCP\Share::isEnabled();
@ -1409,7 +1409,7 @@ class Util {
foreach ($dirContent as $item) {
$filePath = substr($item['path'], 25);
if ($item['type'] == 'dir') {
$this->addRecoveryKey($filePath . '/', $privateKey);
$this->recoverAllFiles($filePath . '/', $privateKey);
} else {
$file = substr($filePath, 0, -4);
$this->recoverFile($file, $privateKey);

View File

@ -62,8 +62,17 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
// Filesystem related hooks
\OCA\Encryption\Helper::registerUserHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
@ -78,6 +87,13 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
function tearDown() {
\OC_FileProxy::clearProxies();
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
}
function testGenerateKey() {
@ -686,7 +702,6 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
$this->assertEquals( $this->dataLong, $newDecrypt );
// tear down
$view->unlink( $newFolder . '/' . $newFilename );
$view->unlink( $newFolder );
}

View File

@ -57,6 +57,12 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
@ -72,6 +78,13 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
\OC_FileProxy::$enabled = true;
\OC_FileProxy::clearProxies();
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
}
function testGetPrivateKey() {
@ -116,6 +129,16 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
Encryption\Keymanager::setFileKey( $this->view, $file, $this->userId, $key['key'] );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = true;
// cleanup
$this->view->unlink('/'.$this->userId . '/files/' . $file);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
}

View File

@ -528,12 +528,152 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
}
function loginHelper($user, $create = false)
function testRecoveryFile()
{
// login as admin
$this->loginHelper('admin');
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'admin');
// check if recovery password match
$this->assertTrue($util->checkRecoveryPassword('test123'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(true));
// create folder structure
$this->view->mkdir('/admin/files' . $this->folder1);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile1));
$this->assertTrue(is_int($cryptedFile2));
// check if share key for admin and recovery exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.admin.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
// disable recovery for admin
$this->assertTrue($util->setRecoveryForUser(false));
// remove all recovery keys
$util->removeRecoveryKeys('/');
// check if share key for recovery not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(true));
// remove all recovery keys
$util->addRecoveryKeys('/');
// check if share key for admin and recovery exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
$this->view->unlink('/admin/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename);
// check if share key for recovery not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
}
function testRecoveryForUser()
{
// login as admin
$this->loginHelper('admin');
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
// login as user1
$this->loginHelper('user1');
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'user1');
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(true));
// create folder structure
$this->view->mkdir('/user1/files' . $this->folder1);
$this->view->mkdir('/user1/files' . $this->folder1 . $this->subfolder);
$this->view->mkdir('/user1/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile1));
$this->assertTrue(is_int($cryptedFile2));
// check if share key for user and recovery exists
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.user1.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
// login as admin
$this->loginHelper('admin');
// change password
\OC_User::setPassword('user1', 'test', 'test123');
// login as user1
$this->loginHelper('user1', false, 'test');
// get file contents
$retrievedCryptedFile1 = file_get_contents('crypt://' . $this->filename);
$retrievedCryptedFile2 = file_get_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile1);
$this->assertEquals($this->dataShort, $retrievedCryptedFile2);
// cleanup
$this->view->unlink('/user1/files' . $this->folder1);
$this->view->unlink('/user1/files' . $this->filename);
// check if share key for user and recovery exists
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.'.$recoveryKeyId.'.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.user1.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.shareKey'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(false));
}
function loginHelper($user, $create = false, $password = false)
{
if ($create) {
\OC_User::createUser($user, $user);
}
if($password === false) {
$password = $user;
}
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
@ -541,7 +681,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
\OC_User::setUserId($user);
$params['uid'] = $user;
$params['password'] = $user;
$params['password'] = $password;
OCA\Encryption\Hooks::login($params);
}
}

View File

@ -1541,34 +1541,31 @@ class Share {
}
public static function post_addToGroup($arguments) {
if(\OC_Config::getValue('installed')) {
// Find the group shares and check if the user needs a unique target
$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?');
$result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid']));
$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`,'
.' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,'
.' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
while ($item = $result->fetchRow()) {
if ($item['item_type'] == 'file' || $item['item_type'] == 'file') {
$itemTarget = null;
} else {
$itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
$arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']);
}
if (isset($item['file_source'])) {
$fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
$arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']);
} else {
$fileTarget = null;
}
// Insert an extra row for the group share if the item or file target is unique for this user
if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) {
$query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'],
self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'],
$item['stime'], $item['file_source'], $fileTarget));
\OC_DB::insertid('*PREFIX*share');
}
// Find the group shares and check if the user needs a unique target
$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?');
$result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid']));
$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`,'
.' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,'
.' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
while ($item = $result->fetchRow()) {
if ($item['item_type'] == 'file' || $item['item_type'] == 'file') {
$itemTarget = null;
} else {
$itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
$arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']);
}
if (isset($item['file_source'])) {
$fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
$arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']);
} else {
$fileTarget = null;
}
// Insert an extra row for the group share if the item or file target is unique for this user
if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) {
$query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'],
self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'],
$item['stime'], $item['file_source'], $fileTarget));
\OC_DB::insertid('*PREFIX*share');
}
}
}