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

Conflicts:
	apps/files/l10n/ar.php
	apps/files/l10n/bn_BD.php
	apps/files/l10n/de.php
	apps/files/l10n/de_DE.php
	apps/files/l10n/el.php
	apps/files/l10n/es.php
	apps/files/l10n/fa.php
	apps/files/l10n/fr.php
	apps/files/l10n/gl.php
	apps/files/l10n/id.php
	apps/files/l10n/ko.php
	apps/files/l10n/nl.php
	apps/files/l10n/nn_NO.php
	apps/files/l10n/pl.php
	apps/files/l10n/pt_BR.php
	apps/files/l10n/pt_PT.php
	apps/files/l10n/ro.php
	apps/files/l10n/ru.php
	apps/files/l10n/sl.php
	apps/files/l10n/uk.php
	apps/files/l10n/vi.php
	apps/files_encryption/l10n/ca.php
	apps/files_sharing/lib/cache.php
	apps/files_sharing/lib/sharedstorage.php
	apps/user_ldap/l10n/tr.php
	core/l10n/cs_CZ.php
	core/l10n/de.php
	core/l10n/de_DE.php
	core/l10n/ru.php
	core/l10n/sk_SK.php
	core/l10n/tr.php
	core/l10n/vi.php
	core/l10n/zh_CN.php
	l10n/af_ZA/files.po
	l10n/ar/files.po
	l10n/be/files.po
	l10n/bg_BG/files.po
	l10n/bn_BD/files.po
	l10n/ca/files.po
	l10n/ca/files_encryption.po
	l10n/cs_CZ/core.po
	l10n/cs_CZ/files.po
	l10n/da/files.po
	l10n/da/settings.po
	l10n/de/core.po
	l10n/de/files.po
	l10n/de/files_encryption.po
	l10n/de/files_external.po
	l10n/de/files_sharing.po
	l10n/de/files_trashbin.po
	l10n/de/files_versions.po
	l10n/de/lib.po
	l10n/de/settings.po
	l10n/de/user_ldap.po
	l10n/de_DE/files.po
	l10n/de_DE/files_encryption.po
	l10n/de_DE/files_external.po
	l10n/de_DE/files_sharing.po
	l10n/de_DE/files_trashbin.po
	l10n/de_DE/lib.po
	l10n/de_DE/settings.po
	l10n/de_DE/user_ldap.po
	l10n/el/files.po
	l10n/eo/files.po
	l10n/es/files.po
	l10n/es/lib.po
	l10n/es/settings.po
	l10n/es_AR/files.po
	l10n/eu/files.po
	l10n/fa/files.po
	l10n/fi/files.po
	l10n/fi_FI/files.po
	l10n/fr/core.po
	l10n/fr/files.po
	l10n/gl/files.po
	l10n/gl/settings.po
	l10n/he/core.po
	l10n/he/files.po
	l10n/he/user_ldap.po
	l10n/hi/files.po
	l10n/hr/files.po
	l10n/hu_HU/core.po
	l10n/hu_HU/files.po
	l10n/hu_HU/files_external.po
	l10n/hu_HU/settings.po
	l10n/hy/files.po
	l10n/ia/core.po
	l10n/ia/files.po
	l10n/ia/files_trashbin.po
	l10n/ia/settings.po
	l10n/id/files.po
	l10n/is/files.po
	l10n/it/files.po
	l10n/ja_JP/files.po
	l10n/ja_JP/user_ldap.po
	l10n/ka/files.po
	l10n/ka_GE/files.po
	l10n/kn/files.po
	l10n/ko/files.po
	l10n/ko/files_trashbin.po
	l10n/ko/settings.po
	l10n/ku_IQ/files.po
	l10n/lb/files.po
	l10n/lt_LT/core.po
	l10n/lt_LT/files.po
	l10n/lv/files.po
	l10n/mk/files.po
	l10n/ms_MY/files.po
	l10n/my_MM/files.po
	l10n/nb_NO/files.po
	l10n/ne/files.po
	l10n/nl/core.po
	l10n/nl/files.po
	l10n/nn_NO/files.po
	l10n/oc/files.po
	l10n/pl/files.po
	l10n/pl_PL/files.po
	l10n/pt_BR/files.po
	l10n/pt_PT/core.po
	l10n/pt_PT/files.po
	l10n/pt_PT/files_external.po
	l10n/pt_PT/settings.po
	l10n/ro/files.po
	l10n/ru/core.po
	l10n/ru/files.po
	l10n/si_LK/files.po
	l10n/sk/files.po
	l10n/sk_SK/core.po
	l10n/sk_SK/files.po
	l10n/sl/core.po
	l10n/sl/files.po
	l10n/sq/files.po
	l10n/sr/files.po
	l10n/sr@latin/files.po
	l10n/sv/files.po
	l10n/sw_KE/files.po
	l10n/ta_LK/files.po
	l10n/te/files.po
	l10n/th_TH/files.po
	l10n/tr/core.po
	l10n/tr/files.po
	l10n/tr/lib.po
	l10n/tr/settings.po
	l10n/tr/user_ldap.po
	l10n/uk/files.po
	l10n/ur_PK/files.po
	l10n/vi/core.po
	l10n/vi/files.po
	l10n/vi/files_external.po
	l10n/zh_CN.GB2312/files.po
	l10n/zh_CN/core.po
	l10n/zh_CN/files.po
	l10n/zh_CN/settings.po
	l10n/zh_HK/files.po
	l10n/zh_TW/core.po
	l10n/zh_TW/files.po
	l10n/zh_TW/files_versions.po
	l10n/zh_TW/settings.po
	l10n/zh_TW/user_ldap.po
	lib/base.php
	lib/files/filesystem.php
	lib/files/view.php
	lib/public/share.php
	settings/l10n/es.php
	settings/l10n/pt_PT.php
	settings/l10n/tr.php
	settings/l10n/zh_CN.php
	settings/personal.php
This commit is contained in:
Florin Peter 2013-05-15 10:18:25 +02:00
commit 01f1153b08
49 changed files with 2475 additions and 843 deletions

View File

@ -1,6 +1,5 @@
setValue( $app, $key, $value )
<?php <?php
/** /**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com> * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later. * This file is licensed under the Affero General Public License version 3 or later.
@ -8,69 +7,79 @@ setValue( $app, $key, $value )
* *
* @brief Script to handle admin settings for encrypted key recovery * @brief Script to handle admin settings for encrypted key recovery
*/ */
use OCA\Encryption; use OCA\Encryption;
\OCP\JSON::checkAdminUser(); \OCP\JSON::checkAdminUser();
\OCP\JSON::checkAppEnabled('files_encryption'); \OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck(); \OCP\JSON::callCheck();
$return = $doSetup = false; $return = false;
// Enable recoveryAdmin
if ( if (
isset($_POST['adminEnableRecovery']) isset($_POST['adminEnableRecovery'])
&& $_POST['adminEnableRecovery'] == 1 && 1 == $_POST['adminEnableRecovery']
) {
$view = new \OC\Files\View('/');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
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"))
&& isset($_POST['recoveryPassword']) && isset($_POST['recoveryPassword'])
&& !empty($_POST['recoveryPassword']) && !empty($_POST['recoveryPassword'])
) { ) {
// TODO: Let the admin set this themselves $keypair = \OCA\Encryption\Crypt::createKeypair();
$recoveryAdminUid = 'recoveryAdmin';
// If desired recoveryAdmin UID is already in use \OC_FileProxy::$enabled = false;
if ( ! \OC_User::userExists( $recoveryAdminUid ) ) {
// Create new recoveryAdmin user // Save public key
\OC_User::createUser( $recoveryAdminUid, $_POST['recoveryPassword'] );
$doSetup = true; if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
} else { $view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
// Get list of admin users // Encrypt private key empthy passphrase
$admins = OC_Group::usersInGroup( 'admin' ); $encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $_POST['recoveryPassword']);
// If the existing recoveryAdmin UID is an admin // Save private key
if ( in_array( $recoveryAdminUid, $admins ) ) { $view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
// The desired recoveryAdmi UID pre-exists and can be used \OC_FileProxy::$enabled = true;
$doSetup = true;
// If the recoveryAdmin UID exists but doesn't have admin rights
} else {
$return = false;
} }
} // Set recoveryAdmin as enabled
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
// If recoveryAdmin has passed other checks
if ( $doSetup ) {
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $recoveryAdminUid );
// Ensure recoveryAdmin is ready for encryption (has usable keypair etc.)
$util->setupServerSide( $_POST['recoveryPassword'] );
// Store the UID in the DB
OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminUid', $recoveryAdminUid );
$return = true; $return = true;
// Disable recoveryAdmin
} elseif (
isset($_POST['adminEnableRecovery'])
&& 0 == $_POST['adminEnableRecovery']
) {
// Set recoveryAdmin as enabled
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0);
$return = true;
} }
} // Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();
($return) ? OC_JSON::success() : OC_JSON::error();

View File

@ -0,0 +1,40 @@
<?php
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*
* @brief Script to handle manual trigger of \OCA\Encryption\Util{}->encryptAll()
*/
use OCA\Encryption;
\OCP\JSON::checkAppEnabled( 'files_encryption' );
\OCP\JSON::callCheck();
$return = false;
if (
isset( $_POST['encryptAll'] )
&& ! empty( $_POST['userPassword'] )
) {
$view = new \OC_FilesystemView( '' );
$userId = \OCP\User::getUser();
$util = new \OCA\Encryption\Util( $view, $userId );
$session = new \OCA\Encryption\Session( $view );
$publicKey = \OCA\Encryption\Keymanager::getPublicKey( $view, $userId );
$path = '/' . $userId . '/' . 'files';
$util->encryptAll( $publicKey, $path, $session->getLegacyKey(), $_POST['userPassword'] );
$return = true;
} else {
$return = false;
}
// Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();

View File

@ -1,5 +1,3 @@
setValue( $app, $key, $value )
<?php <?php
/** /**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com> * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
@ -17,26 +15,21 @@ use OCA\Encryption;
if ( if (
isset( $_POST['userEnableRecovery'] ) isset( $_POST['userEnableRecovery'] )
&& ( 0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'] )
) { ) {
// Ensure preference is an integer
$recoveryEnabled = intval( $_POST['userEnableRecovery'] );
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $userId ); $util = new \OCA\Encryption\Util( $view, $userId );
// Save recovery preference to DB // Save recovery preference to DB
$result = $util->setRecovery( $recoveryEnabled ); $return = $util->setRecoveryForUser( $_POST['userEnableRecovery'] );
if ( $result ) {
\OCP\JSON::success();
} else { } else {
\OCP\JSON::error(); $return = false;
} }
} // Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();

View File

@ -8,24 +8,25 @@ OC::$CLASSPATH['OCA\Encryption\Stream'] = 'files_encryption/lib/stream.php';
OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php'; OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php';
OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php'; OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php'; OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
OC_FileProxy::register( new OCA\Encryption\Proxy() ); OC_FileProxy::register( new OCA\Encryption\Proxy() );
// User-related hooks // User related hooks
OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); OCA\Encryption\Helper::registerUserHooks();
OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
// Sharing-related hooks // Sharing related hooks
OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' ); OCA\Encryption\Helper::registerShareHooks();
OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' );
OCP\Util::connectHook( 'OCP\Share', 'post_unshareAll', 'OCA\Encryption\Hooks', 'postUnshareAll' );
// Webdav-related hooks // Webdav related hooks
OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfileFromClient' ); OCA\Encryption\Helper::registerWebdavHooks();
// Filesystem related hooks
OCA\Encryption\Helper::registerFilesystemHooks();
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' ); stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
$view = new OC_FilesystemView( '/' ); $view = new \OC\Files\View( '/' );
$session = new OCA\Encryption\Session( $view ); $session = new OCA\Encryption\Session( $view );
@ -47,5 +48,6 @@ if (
} }
// Register settings scripts // Register settings scripts
OCP\App::registerAdmin( 'files_encryption', 'settings' ); OCP\App::registerAdmin( 'files_encryption', 'settings-admin' );
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );

View File

@ -27,6 +27,13 @@
<default>0</default> <default>0</default>
<comments>Whether encryption key recovery is enabled</comments> <comments>Whether encryption key recovery is enabled</comments>
</field> </field>
<field>
<name>migrationStatus</name>
<type>boolean</type>
<notnull>true</notnull>
<default>0</default>
<comments>Whether encryption migration has been performed</comments>
</field>
</declaration> </declaration>
</table> </table>
</database> </database>

View File

@ -0,0 +1,10 @@
/* Copyright (c) 2013, Sam Tuke, <samtuke@owncloud.com>
This file is licensed under the Affero General Public License version 3 or later.
See the COPYING-README file. */
#encryptAllError
, #encryptAllSuccess
, #recoveryEnabledError
, #recoveryEnabledSuccess {
display: none;
}

View File

@ -23,10 +23,11 @@
namespace OCA\Encryption; namespace OCA\Encryption;
use OC\Files\Filesystem;
/** /**
* Class for hook specific logic * Class for hook specific logic
*/ */
class Hooks { class Hooks {
// TODO: use passphrase for encrypting private key that is separate to // TODO: use passphrase for encrypting private key that is separate to
@ -47,13 +48,9 @@ class Hooks {
$util = new Util( $view, $params['uid'] ); $util = new Util( $view, $params['uid'] );
// Check files_encryption infrastructure is ready for action // setup user, if user not ready force relogin
if ( ! $util->ready() ) { if(Helper::setupUser($util, $params['password']) === false) {
return false;
\OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
return $util->setupServerSide( $params['password'] );
} }
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
@ -68,9 +65,13 @@ class Hooks {
$session->setPrivateKey( $privateKey, $params['uid'] ); $session->setPrivateKey( $privateKey, $params['uid'] );
//FIXME: disabled because it gets called each time a user do an operation on iPhone // Check if first-run file migration has already been performed
//FIXME: we need a better place doing this and maybe only one time or by user $migrationCompleted = $util->getMigrationStatus();
/*$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
// If migration not yet done
if ( ! $migrationCompleted ) {
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
// Set legacy encryption key if it exists, to support // Set legacy encryption key if it exists, to support
// depreciated encryption system // depreciated encryption system
@ -89,26 +90,62 @@ class Hooks {
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] ); $publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
\OC_FileProxy::$enabled = false;*/ \OC_FileProxy::$enabled = false;
// Encrypt existing user files: // Encrypt existing user files:
// This serves to upgrade old versions of the encryption // This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt) // app (see appinfo/spec.txt)
/*if ( if (
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] ) $util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) { ) {
\OC_Log::write( \OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login' 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO , \OC_Log::INFO
); );
}*/ }
// Register successful migration in DB
$util->setMigrationStatus( 1 );
}
return true; return true;
} }
/**
* @brief setup encryption backend upon user created
* @note This method should never be called for users using client side encryption
*/
public static function postCreateUser( $params ) {
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $params['uid'] );
Helper::setupUser($util, $params['password']);
}
/**
* @brief cleanup encryption backend upon user deleted
* @note This method should never be called for users using client side encryption
*/
public static function postDeleteUser( $params ) {
$view = new \OC_FilesystemView( '/' );
// cleanup public key
$publicKey = '/public-keys/' . $params['uid'] . '.public.key';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view->unlink($publicKey);
\OC_FileProxy::$enabled = $proxyStatus;
}
/** /**
* @brief Change a user's encryption passphrase * @brief Change a user's encryption passphrase
* @param array $params keys: uid, password * @param array $params keys: uid, password
@ -120,7 +157,9 @@ class Hooks {
// the necessary keys) // the necessary keys)
if ( Crypt::mode() == 'server' ) { if ( Crypt::mode() == 'server' ) {
$session = new Session(); $view = new \OC_FilesystemView( '/' );
$session = new Session($view);
// Get existing decrypted private key // Get existing decrypted private key
$privateKey = $session->getPrivateKey(); $privateKey = $session->getPrivateKey();
@ -166,6 +205,35 @@ class Hooks {
} }
/*
* @brief check if files can be encrypted to every user.
*/
public static function preShared($params) {
$users = array();
$view = new \OC\Files\View('/public-keys/');
switch ($params['shareType']) {
case \OCP\Share::SHARE_TYPE_USER:
$users[] = $params['shareWith'];
break;
case \OCP\Share::SHARE_TYPE_GROUP:
$users = \OC_Group::usersInGroup($params['shareWith']);
break;
}
foreach ($users as $user) {
if (!$view->file_exists($user . '.public.key')) {
// Set flag var 'run' to notify emitting
// script that hook execution failed
$params['run']->run = false;
// TODO: Make sure files_sharing provides user
// feedback on failed share
break;
}
}
}
/** /**
* @brief * @brief
*/ */
@ -187,7 +255,9 @@ class Hooks {
// [fileTarget] => /test8 // [fileTarget] => /test8
// [id] => 10 // [id] => 10
// [token] => // [token] =>
// [run] => whether emitting script should continue to run
// TODO: Should other kinds of item be encrypted too? // TODO: Should other kinds of item be encrypted too?
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$view = new \OC_FilesystemView('/'); $view = new \OC_FilesystemView('/');
@ -196,13 +266,55 @@ class Hooks {
$util = new Util($view, $userId); $util = new Util($view, $userId);
$path = $util->fileIdToPath($params['itemSource']); $path = $util->fileIdToPath($params['itemSource']);
//check if this is a reshare action, that's true if the item source is already shared with me //if parent is set, then this is a re-share action
$sharedItem = \OCP\Share::getItemSharedWithBySource($params['itemType'], $params['itemSource']); if ($params['parent']) {
if ($sharedItem) {
// if it is a re-share than the file is located in my Shared folder // get the parent from current share
$path = '/Shared'.$sharedItem['file_target']; $parent = $util->getShareParent($params['parent']);
// if parent is file the it is an 1:1 share
if ($parent['item_type'] === 'file') {
// prefix path with Shared
$path = '/Shared' . $parent['file_target'];
} else { } else {
$path = $util->fileIdToPath($params['itemSource']);
// NOTE: parent is folder but shared was a file!
// we try to rebuild the missing path
// some examples we face here
// user1 share folder1 with user2 folder1 has
// the following structure
// /folder1/subfolder1/subsubfolder1/somefile.txt
// user2 re-share subfolder2 with user3
// user3 re-share somefile.txt user4
// so our path should be
// /Shared/subfolder1/subsubfolder1/somefile.txt
// while user3 is sharing
if ($params['itemType'] === 'file') {
// get target path
$targetPath = $util->fileIdToPath($params['fileSource']);
$targetPathSplit = array_reverse(explode('/', $targetPath));
// init values
$path = '';
$sharedPart = ltrim($parent['file_target'], '/');
// rebuild path
foreach ($targetPathSplit as $pathPart) {
if ($pathPart !== $sharedPart) {
$path = '/' . $pathPart . $path;
} else {
break;
}
}
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $path;
} else {
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $params['fileTarget'];
}
}
} }
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
@ -216,23 +328,7 @@ class Hooks {
foreach ($allFiles as $path) { foreach ($allFiles as $path) {
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path); $usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
$util->setSharedFileKeyfiles( $session, $usersSharing, $path );
$failed = array();
// Attempt to set shareKey
if (!$util->setSharedFileKeyfiles($session, $usersSharing, $path)) {
$failed[] = $path;
}
}
// If no attempts to set keyfiles failed
if (empty($failed)) {
return true;
} else {
return false;
} }
} }
} }
@ -247,30 +343,67 @@ class Hooks {
// [itemSource] => 13 // [itemSource] => 13
// [shareType] => 0 // [shareType] => 0
// [shareWith] => test1 // [shareWith] => test1
// [itemParent] =>
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) { if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView( '/' );
$session = new Session($view);
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId); $util = new Util( $view, $userId);
$path = $util->fileIdToPath( $params['itemSource'] ); $path = $util->fileIdToPath( $params['itemSource'] );
// check if this is a re-share
if ( $params['itemParent'] ) {
// get the parent from current share
$parent = $util->getShareParent( $params['itemParent'] );
// get target path
$targetPath = $util->fileIdToPath( $params['itemSource'] );
$targetPathSplit = array_reverse( explode( '/', $targetPath ) );
// init values
$path = '';
$sharedPart = ltrim( $parent['file_target'], '/' );
// rebuild path
foreach ( $targetPathSplit as $pathPart ) {
if ( $pathPart !== $sharedPart ) {
$path = '/' . $pathPart . $path;
} else {
break;
}
}
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $path;
}
// for group shares get a list of the group members // for group shares get a list of the group members
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) { if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) {
$userIds = \OC_Group::usersInGroup($params['shareWith']); $userIds = \OC_Group::usersInGroup($params['shareWith']);
} else if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_LINK ){
$userIds = array( $util->getPublicShareKeyId() );
} else { } else {
$userIds = array( $params['shareWith'] ); $userIds = array( $params['shareWith'] );
} }
// if we unshare a folder we need a list of all (sub-)files // if we unshare a folder we need a list of all (sub-)files
if ( $params['itemType'] === 'folder' ) { if ( $params['itemType'] === 'folder' ) {
$allFiles = $util->getAllFiles( $path ); $allFiles = $util->getAllFiles( $path );
} else { } else {
$allFiles = array( $path ); $allFiles = array( $path );
} }
foreach ( $allFiles as $path ) { foreach ( $allFiles as $path ) {
// check if the user still has access to the file, otherwise delete share key // check if the user still has access to the file, otherwise delete share key
@ -278,6 +411,7 @@ class Hooks {
// Unshare every user who no longer has access to the file // Unshare every user who no longer has access to the file
$delUsers = array_diff( $userIds, $sharingUsers); $delUsers = array_diff( $userIds, $sharingUsers);
if ( !Keymanager::delShareKey( $view, $delUsers, $path ) ) { if ( !Keymanager::delShareKey( $view, $delUsers, $path ) ) {
$failed[] = $path; $failed[] = $path;
@ -296,9 +430,7 @@ class Hooks {
return false; return false;
} }
} }
} }
/** /**
@ -311,4 +443,86 @@ class Hooks {
} }
/**
* @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
* @param array with oldpath and newpath
*
* This function is connected to the rename signal of OC_Filesystem and adjust the name and location
* of the stored versions along the actual file
*/
public static function postRename($params) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/');
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
// Format paths to be relative to user files dir
$oldKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']);
$newKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']);
// add key ext if this is not an folder
if (!$view->is_dir($oldKeyfilePath)) {
$oldKeyfilePath .= '.key';
$newKeyfilePath .= '.key';
// handle share-keys
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$params['oldpath']);
$matches = glob(preg_quote($localKeyPath).'*.shareKey');
foreach ($matches as $src) {
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
// create destination folder if not exists
if(!file_exists(dirname($dst))) {
mkdir(dirname($dst), 0750, true);
}
rename($src, $dst);
}
} else {
// handle share-keys folders
$oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']);
$newShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']);
// create destination folder if not exists
if(!$view->file_exists(dirname($newShareKeyfilePath))) {
$view->mkdir(dirname($newShareKeyfilePath), 0750, true);
}
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
}
// Rename keyfile so it isn't orphaned
if($view->file_exists($oldKeyfilePath)) {
// create destination folder if not exists
if(!$view->file_exists(dirname($newKeyfilePath))) {
$view->mkdir(dirname($newKeyfilePath), 0750, true);
}
$view->rename($oldKeyfilePath, $newKeyfilePath);
}
// build the path to the file
$newPath = '/' . $userId . '/files' .$params['newpath'];
$newPathRelative = $params['newpath'];
if($util->fixFileSize($newPath)) {
// get sharing app state
$sharingEnabled = \OCP\Share::isEnabled();
// get users
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
// update sharing-keys
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
} }

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>, Robin Appelman
* <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
$(document).ready(function(){
// Trigger ajax on recoveryAdmin status change
$( 'input:radio[name="adminEnableRecovery"]' ).change(
function() {
var recoveryStatus = $( this ).val();
var recoveryPassword = $( '#recoveryPassword' ).val();
if ( '' == recoveryPassword ) {
// FIXME: add proper OC notification
alert( 'You must set a recovery account password first' );
} else {
$.post(
OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' )
, { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword }
, function( data ) {
alert( data );
}
);
}
}
);
function blackListChange(){
var blackList=$( '#encryption_blacklist' ).val().join( ',' );
OC.AppConfig.setValue( 'files_encryption', 'type_blacklist', blackList );
}
})

View File

@ -0,0 +1,60 @@
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
$(document).ready(function(){
// Trigger ajax on recoveryAdmin status change
$( 'input:radio[name="userEnableRecovery"]' ).change(
function() {
// Hide feedback messages in case they're already visible
$('#recoveryEnabledSuccess').hide();
$('#recoveryEnabledError').hide();
var recoveryStatus = $( this ).val();
$.post(
OC.filePath( 'files_encryption', 'ajax', 'userrecovery.php' )
, { userEnableRecovery: recoveryStatus }
, function( data ) {
if ( data.status == "success" ) {
$('#recoveryEnabledSuccess').show();
} else {
$('#recoveryEnabledError').show();
}
}
);
// Ensure page is not reloaded on form submit
return false;
}
);
$("#encryptAll").click(
function(){
// Hide feedback messages in case they're already visible
$('#encryptAllSuccess').hide();
$('#encryptAllError').hide();
var userPassword = $( '#userPassword' ).val();
var encryptAll = $( '#encryptAll' ).val();
$.post(
OC.filePath( 'files_encryption', 'ajax', 'encryptall.php' )
, { encryptAll: encryptAll, userPassword: userPassword }
, function( data ) {
if ( data.status == "success" ) {
$('#encryptAllSuccess').show();
} else {
$('#encryptAllError').show();
}
}
);
// Ensure page is not reloaded on form submit
return false;
}
);
})

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
$(document).ready(function(){
// Trigger ajax on filetype blacklist change
$('#encryption_blacklist').multiSelect({
oncheck:blackListChange,
onuncheck:blackListChange,
createText:'...'
});
// Trigger ajax on recoveryAdmin status change
$( 'input:radio[name="adminEnableRecovery"]' ).change(
function() {
var foo = $( this ).val();
$.post(
OC.filePath('files_encryption', 'ajax', 'adminrecovery.php')
, { adminEnableRecovery: foo, recoveryPassword: 'password' }
, function( data ) {
alert( data );
}
);
}
);
function blackListChange(){
var blackList=$('#encryption_blacklist').val().join(',');
OC.AppConfig.setValue('files_encryption','type_blacklist',blackList);
}
})

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert", "File encryption is enabled." => "Dateiverschlüsselung ist aktiviert",
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", "The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:", "Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:",
"None" => "Nichts" "None" => "Keine"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert", "File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", "The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:", "Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:",
"None" => "Nichts" "None" => "Keine"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.", "File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.",
"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:", "The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:",
"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:", "Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:",
"None" => "Τίποτα" "None" => "Καμία"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.", "File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:", "The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:",
"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:", "Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:",
"None" => "Ezer" "None" => "Bat ere ez"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "La cifratura dei file è abilitata.", "File encryption is enabled." => "La cifratura dei file è abilitata.",
"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:", "The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:",
"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:", "Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:",
"None" => "Nessuno" "None" => "Nessuna"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Szyfrowanie plików jest włączone", "File encryption is enabled." => "Szyfrowanie plików jest włączone",
"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:", "The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:",
"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:", "Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:",
"None" => "Nic" "None" => "Brak"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "A criptografia de arquivos está ativada.", "File encryption is enabled." => "A criptografia de arquivos está ativada.",
"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:", "The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:",
"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:", "Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:",
"None" => "Nada" "None" => "Nenhuma"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Шифрование файла включено.", "File encryption is enabled." => "Шифрование файла включено.",
"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:", "The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:",
"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:", "Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:",
"None" => "Нет новостей" "None" => "Ничего"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Šifrovanie súborov nastavené.", "File encryption is enabled." => "Šifrovanie súborov nastavené.",
"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:", "The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:",
"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov", "Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov",
"None" => "Žiadny" "None" => "Žiadne"
); );

View File

@ -1,4 +1,4 @@
<?php $TRANSLATIONS = array( <?php $TRANSLATIONS = array(
"Encryption" => "การเข้ารหัส", "Encryption" => "การเข้ารหัส",
"None" => "ไม่มี" "None" => "ไม่ต้อง"
); );

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Mã hóa file đã mở", "File encryption is enabled." => "Mã hóa file đã mở",
"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa", "The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa",
"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau", "Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau",
"None" => "Không gì cả" "None" => "Không có gì hết"
); );

View File

@ -0,0 +1,93 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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;
/**
* @brief Class to manage registration of hooks an various helper methods
*/
class Helper {
/**
* @brief register share related hooks
*
*/
public static function registerShareHooks() {
\OCP\Util::connectHook( 'OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared' );
\OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' );
\OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' );
\OCP\Util::connectHook( 'OCP\Share', 'post_unshareAll', 'OCA\Encryption\Hooks', 'postUnshareAll' );
}
/**
* @brief register user related hooks
*
*/
public static function registerUserHooks() {
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
\OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' );
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' );
}
/**
* @brief register webdav related hooks
*
*/
public static function registerWebdavHooks() {
\OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfileFromClient' );
}
/**
* @brief register filesystem related hooks
*
*/
public static function registerFilesystemHooks() {
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
}
/**
* @brief setup user for files_encryption
*
* @param Util $util
* @param string $password
* @return bool
*/
public static function setupUser($util, $password) {
// Check files_encryption infrastructure is ready for action
if ( ! $util->ready() ) {
\OC_Log::write( 'Encryption library', 'User account "' . $util->getUserId() . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
if(!$util->setupServerSide( $password )) {
return false;
}
}
return true;
}
}

View File

@ -32,15 +32,20 @@ class Keymanager {
/** /**
* @brief retrieve the ENCRYPTED private key from a user * @brief retrieve the ENCRYPTED private key from a user
* *
* @return string private key or false * @return string private key or false (hopefully)
* @note the key returned by this method must be decrypted before use * @note the key returned by this method must be decrypted before use
*/ */
public static function getPrivateKey( \OC_FilesystemView $view, $user ) { public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key'; $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$key = $view->file_get_contents( $path ); $key = $view->file_get_contents( $path );
\OC_FileProxy::$enabled = $proxyStatus;
return $key; return $key;
} }
@ -125,13 +130,24 @@ class Keymanager {
// create all parent folders // create all parent folders
$info = pathinfo( $basePath . '/' . $targetPath ); $info = pathinfo( $basePath . '/' . $targetPath );
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] ); $keyfileFolderName = $view->getLocalFolder( $info['dirname'] );
if ( ! file_exists( $keyfileFolderName ) ) { if ( ! file_exists( $keyfileFolderName ) ) {
mkdir( $keyfileFolderName, 0750, true ); mkdir( $keyfileFolderName, 0750, true );
} }
} }
// try reusing key file if part file
if ( self::isPartialFilePath( $targetPath ) ) {
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile );
} else {
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); $result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
}
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -139,6 +155,47 @@ class Keymanager {
} }
/**
* @brief 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 static function fixPartialFilePath( $path ) {
if (preg_match('/\.part$/', $path)) {
$newLength = strlen($path) - 5;
$fPath = substr($path, 0, $newLength);
return $fPath;
} else {
return $path;
}
}
/**
* @brief Check if a path is a .part file
* @param string $path Path that may identify a .part file
* @return bool
*/
public static function isPartialFilePath( $path ) {
if ( preg_match('/\.part$/', $path ) ) {
return true;
} else {
return false;
}
}
/** /**
* @brief retrieve keyfile for an encrypted file * @brief retrieve keyfile for an encrypted file
* @param \OC_FilesystemView $view * @param \OC_FilesystemView $view
@ -151,6 +208,19 @@ class Keymanager {
*/ */
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) {
// try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) {
$result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) );
if ( $result ) {
return $result;
}
}
$util = new Util($view, \OCP\User::getUser()); $util = new Util($view, \OCP\User::getUser());
list($owner, $filename) = $util->getUidAndFilename($filePath); list($owner, $filename) = $util->getUidAndFilename($filePath);
$filePath_f = ltrim( $filename, '/' ); $filePath_f = ltrim( $filename, '/' );
@ -236,6 +306,7 @@ class Keymanager {
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
return $result; return $result;
} }
/** /**
@ -287,7 +358,7 @@ class Keymanager {
*/ */
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) {
//here we need the currently logged in user, while userId can be a different user // Here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $path ); list( $owner, $filename ) = $util->getUidAndFilename( $path );
@ -296,8 +367,17 @@ class Keymanager {
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner ); $shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
// try reusing key file if part file
if(self::isPartialFilePath($shareKeyPath)) {
$writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey';
} else {
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey'; $writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
}
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
@ -359,6 +439,19 @@ class Keymanager {
*/ */
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) {
// try reusing key file if part file
if(self::isPartialFilePath($filePath)) {
$result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath));
if($result) {
return $result;
}
}
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
@ -367,7 +460,7 @@ class Keymanager {
list($owner, $filename) = $util->getUidAndFilename($filePath); list($owner, $filename) = $util->getUidAndFilename($filePath);
$shareKeyPath = '/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey'; $shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey');
if ( $view->file_exists( $shareKeyPath ) ) { if ( $view->file_exists( $shareKeyPath ) ) {
$result = $view->file_get_contents( $shareKeyPath ); $result = $view->file_get_contents( $shareKeyPath );

View File

@ -99,9 +99,9 @@ class Proxy extends \OC_FileProxy {
if ( !is_resource( $data ) ) { if ( !is_resource( $data ) ) {
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$rootView = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView( '/' );
$util = new Util( $rootView, $userId ); $util = new Util( $view, $userId );
$session = new Session( $rootView ); $session = new Session( $view );
$privateKey = $session->getPrivateKey(); $privateKey = $session->getPrivateKey();
$filePath = $util->stripUserFilesPath( $path ); $filePath = $util->stripUserFilesPath( $path );
// Set the filesize for userland, before encrypting // Set the filesize for userland, before encrypting
@ -112,10 +112,10 @@ class Proxy extends \OC_FileProxy {
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Check if there is an existing key we can reuse // Check if there is an existing key we can reuse
if ( $encKeyfile = Keymanager::getFileKey( $rootView, $userId, $filePath ) ) { if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) {
// Fetch shareKey // Fetch shareKey
$shareKey = Keymanager::getShareKey( $rootView, $userId, $filePath ); $shareKey = Keymanager::getShareKey( $view, $userId, $filePath );
// Decrypt the keyfile // Decrypt the keyfile
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); $plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
@ -135,25 +135,25 @@ class Proxy extends \OC_FileProxy {
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId ); $uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId );
// Fetch public keys for all users who will share the file // Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $rootView, $uniqueUserIds ); $publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds );
// Encrypt plain keyfile to multiple sharefiles // Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); $multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys );
// Save sharekeys to user folders // Save sharekeys to user folders
Keymanager::setShareKeys( $rootView, $filePath, $multiEncrypted['keys'] ); Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] );
// Set encrypted keyfile as common varname // Set encrypted keyfile as common varname
$encKey = $multiEncrypted['data']; $encKey = $multiEncrypted['data'];
// Save keyfile for newly encrypted file in parallel directory tree // Save keyfile for newly encrypted file in parallel directory tree
Keymanager::setFileKey( $rootView, $filePath, $userId, $encKey ); Keymanager::setFileKey( $view, $filePath, $userId, $encKey );
// Replace plain content with encrypted content by reference // Replace plain content with encrypted content by reference
$data = $encData; $data = $encData;
// Update the file cache with file info // Update the file cache with file info
\OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' ); \OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted'=>true, 'size' => strlen($size), 'unencrypted_size' => $size), '' );
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -162,6 +162,7 @@ class Proxy extends \OC_FileProxy {
} }
return true; return true;
} }
/** /**
@ -277,132 +278,18 @@ class Proxy extends \OC_FileProxy {
* @return bool Result of rename() * @return bool Result of rename()
* @note This is pre rather than post because using post didn't work * @note This is pre rather than post because using post didn't work
*/ */
public function preRename( $oldPath, $newPath ) public function postWrite( $path )
{ {
$this->handleFile($path);
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/');
$userId = \OCP\USER::getUser();
// Format paths to be relative to user files dir
$oldTrimmed = ltrim($oldPath, '/');
$oldSplit = explode('/', $oldTrimmed);
$oldSliced = array_slice($oldSplit, 2);
$oldRelPath = implode('/', $oldSliced);
$oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath;
$newTrimmed = ltrim($newPath, '/');
$newSplit = explode('/', $newTrimmed);
$newSliced = array_slice($newSplit, 2);
$newRelPath = implode('/', $newSliced);
$newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath;
// add key ext if this is not an folder
if (!$view->is_dir($oldKeyfilePath)) {
$oldKeyfilePath .= '.key';
$newKeyfilePath .= '.key';
// handle share-keys
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$oldRelPath);
$matches = glob(preg_quote($localKeyPath).'*.shareKey');
foreach ($matches as $src) {
$dst = str_replace($oldRelPath, $newRelPath, $src);
rename($src, $dst);
}
} else {
// handle share-keys folders
$oldShareKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $oldRelPath;
$newShareKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $newRelPath;
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
}
// Rename keyfile so it isn't orphaned
$result = $view->rename($oldKeyfilePath, $newKeyfilePath);
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief When a file is renamed, rename its keyfile also
* @return bool Result of rename()
* @note This is pre rather than post because using post didn't work
*/
public function postRename( $oldPath, $newPath )
{
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/');
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
// Reformat path for use with OC_FSV
$newPathSplit = explode( '/', $newPath );
$newPathRelative = implode( '/', array_slice( $newPathSplit, 3 ) );
// get file info from database/cache
//$newFileInfo = \OC\Files\Filesystem::getFileInfo($newPathRelative);
if ($util->isEncryptedPath($newPath)) {
$cached = $view->getFileInfo($newPath);
$cached['encrypted'] = 1;
// get the size from filesystem
$size = $view->filesize($newPath);
// calculate last chunk nr
$lastChunckNr = floor($size / 8192);
// open stream
$result = fopen('crypt://' . $newPathRelative, "r");
if(is_resource($result)) {
// calculate last chunk position
$lastChunckPos = ($lastChunckNr * 8192);
// seek to end
fseek($result, $lastChunckPos);
// get the content of the last chunck
$lastChunkContent = fread($result, 8192);
// calc the real file size with the size of the last chunk
$realSize = (($lastChunckNr * 6126) + strlen($lastChunkContent));
// set the size
$cached['unencrypted_size'] = $realSize;
}
$view->putFileInfo( $newPath, $cached );
// get sharing app state
$sharingEnabled = \OCP\Share::isEnabled();
// get users
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
// update sharing-keys
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
}
\OC_FileProxy::$enabled = $proxyStatus;
return true; return true;
}
public function postTouch( $path )
{
$this->handleFile($path);
return true;
} }
public function postFopen( $path, &$result ){ public function postFopen( $path, &$result ){
@ -518,20 +405,23 @@ class Proxy extends \OC_FileProxy {
return $data; return $data;
} }
public function postStat( $path, $data ) { public function postStat($path, $data)
{
// check if file is encrypted
if (Crypt::isCatfileContent($path)) { if (Crypt::isCatfileContent($path)) {
// get file info from cache
$cached = \OC\Files\Filesystem::getFileInfo($path, ''); $cached = \OC\Files\Filesystem::getFileInfo($path, '');
// set the real file size
$data['size'] = $cached['unencrypted_size']; $data['size'] = $cached['unencrypted_size'];
} }
return $data; return $data;
} }
public function postFileSize( $path, $size ) { public function postFileSize($path, $size)
{
$view = new \OC_FilesystemView('/'); $view = new \OC_FilesystemView('/');
@ -544,14 +434,65 @@ class Proxy extends \OC_FileProxy {
$path_split = explode('/', $path); $path_split = explode('/', $path);
$path_f = implode('/', array_slice($path_split, 3)); $path_f = implode('/', array_slice($path_split, 3));
// if path is empty we cannot resolve anything
if(empty($path_f)) {
return $size;
}
// get file info from database/cache // get file info from database/cache
$fileInfo = \OC\Files\Filesystem::getFileInfo($path_f); $fileInfo = \OC\Files\Filesystem::getFileInfo($path_f);
// if file is encrypted return real file size // if file is encrypted return real file size
if(is_array($fileInfo) && $fileInfo['encrypted'] == 1) { if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
return $fileInfo['unencrypted_size']; $size = $fileInfo['unencrypted_size'];
} else { } else {
// self healing if file was removed from file cache
if(is_array($fileInfo)) {
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
$fixSize = $util->getFileSize($path);
if($fixSize > 0) {
$size = $fixSize;
$fileInfo['encrypted'] = true;
$fileInfo['unencrypted_size'] = $size;
// put file info
$view->putFileInfo( $path_f, $fileInfo );
}
}
}
return $size; return $size;
} }
public function handleFile($path) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/');
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
// Reformat path for use with OC_FSV
$path_split = explode( '/', $path );
$path_f = implode( '/', array_slice( $path_split, 3 ) );
// only if file is on 'files' folder fix file size and sharing
if($path_split[2] == 'files' && $util->fixFileSize($path)) {
// get sharing app state
$sharingEnabled = \OCP\Share::isEnabled();
// get users
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path_f);
// update sharing-keys
$util->setSharedFileKeyfiles($session, $usersSharing, $path_f);
}
\OC_FileProxy::$enabled = $proxyStatus;
} }
} }

View File

@ -35,7 +35,7 @@ class Session {
* *
* The ownCloud key pair is used to allow public link sharing even if encryption is enabled * The ownCloud key pair is used to allow public link sharing even if encryption is enabled
*/ */
public function __construct( \OC_FilesystemView $view ) { public function __construct( $view ) {
$this->view = $view; $this->view = $view;
@ -43,16 +43,25 @@ class Session {
if ( ! $this->view->is_dir( 'owncloud_private_key' ) ) { if ( ! $this->view->is_dir( 'owncloud_private_key' ) ) {
$this->view->mkdir( 'owncloud_private_key' ); $this->view->mkdir( 'owncloud_private_key' );
} }
$publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
if ($publicShareKeyId === null) {
$publicShareKeyId = 'pubShare_'.substr(md5(time()),0,8);
\OC_Appconfig::setValue('files_encryption', 'publicShareKeyId', $publicShareKeyId);
}
if ( if (
! $this->view->file_exists("/public-keys/owncloud.public.key") ! $this->view->file_exists( "/public-keys/".$publicShareKeyId.".public.key" )
|| ! $this->view->file_exists("/owncloud_private_key/owncloud.private.key" ) || ! $this->view->file_exists( "/owncloud_private_key/".$publicShareKeyId.".private.key" )
) { ) {
$keypair = Crypt::createKeypair(); $keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Save public key // Save public key
@ -61,17 +70,29 @@ class Session {
$view->mkdir('/public-keys'); $view->mkdir('/public-keys');
} }
$this->view->file_put_contents( '/public-keys/owncloud.public.key', $keypair['publicKey'] ); $this->view->file_put_contents( '/public-keys/'.$publicShareKeyId.'.public.key', $keypair['publicKey'] );
// Encrypt private key empthy passphrase // Encrypt private key empthy passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' ); $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
// Save private key // Save private key
$this->view->file_put_contents( '/owncloud_private_key/owncloud.private.key', $encryptedPrivateKey ); $this->view->file_put_contents( '/owncloud_private_key/'.$publicShareKeyId.'.private.key', $encryptedPrivateKey );
\OC_FileProxy::$enabled = true; \OC_FileProxy::$enabled = $proxyStatus;
} }
if(\OCP\USER::getUser() === false) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/'.$publicShareKeyId.'.private.key' );
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' );
$this->setPrivateKey($privateKey);
\OC_FileProxy::$enabled = $proxyStatus;
}
} }
/** /**

View File

@ -73,19 +73,19 @@ class Stream {
public function stream_open( $path, $mode, $options, &$opened_path ) { public function stream_open( $path, $mode, $options, &$opened_path ) {
$this->userId = \OCP\User::getUser();
if ( ! isset( $this->rootView ) ) { if ( ! isset( $this->rootView ) ) {
$this->rootView = new \OC_FilesystemView( '/' ); $this->rootView = new \OC_FilesystemView( '/' );
} }
$util = new Util( $this->rootView, \OCP\USER::getUser());
$this->userId = $util->getUserId();
// Strip identifier text from path, this gives us the path relative to data/<user>/files // Strip identifier text from path, this gives us the path relative to data/<user>/files
$this->relPath = str_replace( 'crypt://', '', $path ); $this->relPath = \OC\Files\Filesystem::normalizePath(str_replace( 'crypt://', '', $path ));
// rawPath is relative to the data directory // rawPath is relative to the data directory
$this->rawPath = $this->userId . '/files/' . $this->relPath; $this->rawPath = $util->getUserFilesDir() . $this->relPath;
if ( if (
dirname( $this->rawPath ) == 'streams' dirname( $this->rawPath ) == 'streams'
@ -298,7 +298,8 @@ class Stream {
// automatically attempted when the file is written to disk - // automatically attempted when the file is written to disk -
// we are handling that separately here and we don't want to // we are handling that separately here and we don't want to
// get into an infinite loop // get into an infinite loop
//\OC_FileProxy::$enabled = false; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get the length of the unencrypted data that we are handling // Get the length of the unencrypted data that we are handling
$length = strlen( $data ); $length = strlen( $data );
@ -322,30 +323,7 @@ class Stream {
} }
// Fetch user's public key
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
// Check if OC sharing api is enabled
$sharingEnabled = \OCP\Share::isEnabled();
$util = new Util( $this->rootView, $this->userId );
// Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId);
// Fetch public keys for all sharing users
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
// Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
$view = new \OC_FilesystemView( '/' );
// Save the new encrypted file key
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] );
// Save the sharekeys
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
// If extra data is left over from the last round, make sure it // If extra data is left over from the last round, make sure it
// is integrated into the next 6126 / 8192 block // is integrated into the next 6126 / 8192 block
@ -437,6 +415,8 @@ class Stream {
$this->size = max( $this->size, $pointer + $length ); $this->size = max( $this->size, $pointer + $length );
$this->unencryptedSize += $length; $this->unencryptedSize += $length;
\OC_FileProxy::$enabled = $proxyStatus;
return $length; return $length;
} }
@ -498,8 +478,53 @@ class Stream {
if ( if (
$this->meta['mode']!='r' $this->meta['mode']!='r'
and $this->meta['mode']!='rb' and $this->meta['mode']!='rb'
and $this->size > 0
) { ) {
\OC\Files\Filesystem::putFileInfo( $this->relPath, array( 'encrypted' => 1, 'size' => $this->size, 'unencrypted_size' => $this->unencryptedSize ), '' ); // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Fetch user's public key
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
// Check if OC sharing api is enabled
$sharingEnabled = \OCP\Share::isEnabled();
$util = new Util( $this->rootView, $this->userId );
// Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId);
// Fetch public keys for all sharing users
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
// Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
$view = new \OC_FilesystemView( '/' );
// Save the new encrypted file key
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] );
// Save the sharekeys
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
// get file info
$fileInfo = $view->getFileInfo($this->rawPath);
if(!is_array($fileInfo)) {
$fileInfo = array();
}
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// set encryption data
$fileInfo['encrypted'] = true;
$fileInfo['size'] = $this->size;
$fileInfo['unencrypted_size'] = $this->unencryptedSize;
// set fileinfo
$view->putFileInfo( $this->rawPath, $fileInfo);
} }
return fclose( $this->handle ); return fclose( $this->handle );

View File

@ -3,8 +3,8 @@
* ownCloud * ownCloud
* *
* @author Sam Tuke, Frank Karlitschek * @author Sam Tuke, Frank Karlitschek
* @copyright 2012 Sam Tuke samtuke@owncloud.com, * @copyright 2012 Sam Tuke <samtuke@owncloud.com>,
* Frank Karlitschek frank@owncloud.org * Frank Karlitschek <frank@owncloud.org>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@ -24,11 +24,8 @@
# Bugs # Bugs
# ---- # ----
# Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer # Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer
# Timeouts on first login due to encryption of very large files (fix in progress, as a result streaming is currently broken)
# Sharing all files to admin for recovery purposes still in progress # Sharing all files to admin for recovery purposes still in progress
# Possibly public links are broken (not tested since last merge of master) # Possibly public links are broken (not tested since last merge of master)
# encryptAll during login mangles paths: /files/files/
# encryptAll is accessing files via encryption proxy - perhaps proxies should be disabled?
# Missing features # Missing features
@ -91,20 +88,13 @@ class Util {
//// TODO: add support for optional recovery in case of lost passphrase / keys //// TODO: add support for optional recovery in case of lost passphrase / keys
//// TODO: add admin optional required long passphrase for users //// TODO: add admin optional required long passphrase for users
//// TODO: add UI buttons for encrypt / decrypt everything
//// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc. //// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
// Sharing:
//// TODO: add support for encrypting to multiple public keys
//// TODO: add support for decrypting to multiple private keys
// Integration testing: // Integration testing:
//// TODO: test new encryption with versioning //// TODO: test new encryption with versioning
//// TODO: test new encryption with sharing //// DONE: test new encryption with sharing
//// TODO: test new encryption with proxies //// TODO: test new encryption with proxies
@ -118,12 +108,39 @@ class Util {
private $shareKeysPath; // Dir containing env keys for shared files private $shareKeysPath; // Dir containing env keys for shared files
private $publicKeyPath; // Path to user's public key private $publicKeyPath; // Path to user's public key
private $privateKeyPath; // Path to user's private key private $privateKeyPath; // Path to user's private key
private $publicShareKeyId;
private $recoveryKeyId;
private $isPublic;
public function __construct( \OC_FilesystemView $view, $userId, $client = false ) { public function __construct( \OC_FilesystemView $view, $userId, $client = false ) {
$this->view = $view; $this->view = $view;
$this->userId = $userId; $this->userId = $userId;
$this->client = $client; $this->client = $client;
$this->isPublic = false;
$this->publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
$this->recoveryKeyId = \OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// if we are anonymous/public
if($this->userId === false) {
$this->userId = $this->publicShareKeyId;
// only handle for files_sharing app
if($GLOBALS['app'] === 'files_sharing') {
$this->userDir = '/' . $GLOBALS['fileOwner'];
$this->fileFolderName = 'files';
$this->userFilesDir = '/' . $GLOBALS['fileOwner'] . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $GLOBALS['fileOwner'] . '/' . 'files_encryption';
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
$this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
$this->privateKeyPath = '/owncloud_private_key/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
$this->isPublic = true;
}
} else {
$this->userDir = '/' . $this->userId; $this->userDir = '/' . $this->userId;
$this->fileFolderName = 'files'; $this->fileFolderName = 'files';
$this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? $this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
@ -133,7 +150,7 @@ class Util {
$this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
}
} }
public function ready() { public function ready() {
@ -207,17 +224,32 @@ class Util {
} }
// If there's no record for this user's encryption preferences
if ( false === $this->recoveryEnabledForUser() ) {
// create database configuration
$sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery`) VALUES (?,?,?)';
$args = array( $this->userId, 'server-side', 0);
$query = \OCP\DB::prepare( $sql );
$query->execute( $args );
}
return true; return true;
} }
public function getPublicShareKeyId() {
return $this->publicShareKeyId;
}
/** /**
* @brief Check whether pwd recovery is enabled for a given user * @brief Check whether pwd recovery is enabled for a given user
* @return bool * @return 1 = yes, 0 = no, false = no record
* @note If records are not being returned, check for a hidden space * @note If records are not being returned, check for a hidden space
* at the start of the uid in db * at the start of the uid in db
*/ */
public function recoveryEnabled() { public function recoveryEnabledForUser() {
$sql = 'SELECT $sql = 'SELECT
recovery recovery
@ -232,16 +264,25 @@ class Util {
$result = $query->execute( $args ); $result = $query->execute( $args );
// Set default in case no records found $recoveryEnabled = array();
$recoveryEnabled = 0;
while( $row = $result->fetchRow() ) { while( $row = $result->fetchRow() ) {
$recoveryEnabled = $row['recovery']; $recoveryEnabled[] = $row['recovery'];
} }
return $recoveryEnabled; // If no record is found
if ( empty( $recoveryEnabled ) ) {
return false;
// If a record is found
} else {
return $recoveryEnabled[0];
}
} }
@ -250,7 +291,21 @@ class Util {
* @param bool $enabled Whether to enable or disable recovery * @param bool $enabled Whether to enable or disable recovery
* @return bool * @return bool
*/ */
public function setRecovery( $enabled ) { public function setRecoveryForUser( $enabled ) {
$recoveryStatus = $this->recoveryEnabledForUser();
// If a record for this user already exists, update it
if ( false === $recoveryStatus ) {
$sql = 'INSERT INTO `*PREFIX*encryption`
(`uid`,`mode`,`recovery`)
VALUES (?,?,?)';
$args = array( $this->userId, 'server-side', $enabled );
// Create a new record instead
} else {
$sql = 'UPDATE $sql = 'UPDATE
*PREFIX*encryption *PREFIX*encryption
@ -259,11 +314,10 @@ class Util {
WHERE WHERE
uid = ?'; uid = ?';
// Ensure value is an integer
$enabled = intval( $enabled );
$args = array( $enabled, $this->userId ); $args = array( $enabled, $this->userId );
}
$query = \OCP\DB::prepare( $sql ); $query = \OCP\DB::prepare( $sql );
if ( $query->execute( $args ) ) { if ( $query->execute( $args ) ) {
@ -282,7 +336,6 @@ class Util {
* @brief Find all files and their encryption status within a directory * @brief Find all files and their encryption status within a directory
* @param string $directory The path of the parent directory to search * @param string $directory The path of the parent directory to search
* @return mixed false if 0 found, array on success. Keys: name, path * @return mixed false if 0 found, array on success. Keys: name, path
* @note $directory needs to be a path relative to OC data dir. e.g. * @note $directory needs to be a path relative to OC data dir. e.g.
* /admin/files NOT /backup OR /home/www/oc/data/admin/files * /admin/files NOT /backup OR /home/www/oc/data/admin/files
*/ */
@ -427,20 +480,117 @@ class Util {
*/ */
public function isEncryptedPath( $path ) { public function isEncryptedPath( $path ) {
// Disable encryption proxy so data retreived is in its // Disable encryption proxy so data retrieved is in its
// original form // original form
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$data = $this->view->file_get_contents( $path ); // we only need 24 byte from the last chunk
$data = '';
$handle = $this->view->fopen( $path, 'r' );
if(!fseek($handle, -24, SEEK_END)) {
$data = fgets($handle);
}
\OC_FileProxy::$enabled = true; // re-enable proxy
\OC_FileProxy::$enabled = $proxyStatus;
return Crypt::isCatfileContent( $data ); return Crypt::isCatfileContent( $data );
} }
/**
* @brief get the file size of the unencrypted file
* @param $path absolute path
* @return bool
*/
public function getFileSize( $path ) {
$result = 0;
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Reformat path for use with OC_FSV
$pathSplit = explode( '/', $path );
$pathRelative = implode( '/', array_slice( $pathSplit, 3 ) );
if ($pathSplit[2] == 'files' && $this->view->file_exists($path) && $this->isEncryptedPath($path)) {
// get the size from filesystem
$fullPath = $this->view->getLocalFile($path);
$size = filesize($fullPath);
// calculate last chunk nr
$lastChunckNr = floor($size / 8192);
// open stream
$stream = fopen('crypt://' . $pathRelative, "r");
if(is_resource($stream)) {
// calculate last chunk position
$lastChunckPos = ($lastChunckNr * 8192);
// seek to end
fseek($stream, $lastChunckPos);
// get the content of the last chunk
$lastChunkContent = fread($stream, 8192);
// calc the real file size with the size of the last chunk
$realSize = (($lastChunckNr * 6126) + strlen($lastChunkContent));
// store file size
$result = $realSize;
}
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief fix the file size of the encrypted file
* @param $path absolute path
* @return true / false if file is encrypted
*/
public function fixFileSize( $path ) {
$result = false;
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$realSize = $this->getFileSize( $path );
if ( $realSize > 0 ) {
$cached = $this->view->getFileInfo( $path );
$cached['encrypted'] = true;
// set the size
$cached['unencrypted_size'] = $realSize;
// put file info
$this->view->putFileInfo( $path, $cached );
$result = true;
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/** /**
* @brief Format a path to be relative to the /user/files/ directory * @brief Format a path to be relative to the /user/files/ directory
* @note e.g. turns '/admin/files/test.txt' into 'test.txt'
*/ */
public function stripUserFilesPath( $path ) { public function stripUserFilesPath( $path ) {
@ -453,6 +603,21 @@ class Util {
} }
/**
* @brief Format a path to be relative to the /user directory
* @note e.g. turns '/admin/files/test.txt' into 'files/test.txt'
*/
public function stripFilesPath( $path ) {
$trimmed = ltrim( $path, '/' );
$split = explode( '/', $trimmed );
$sliced = array_slice( $split, 1 );
$relPath = implode( '/', $sliced );
return $relPath;
}
/** /**
* @brief Format a shared path to be relative to the /user/files/ directory * @brief Format a shared path to be relative to the /user/files/ directory
* @note Expects a path like /uid/files/Shared/filepath * @note Expects a path like /uid/files/Shared/filepath
@ -665,8 +830,9 @@ class Util {
// Check that the user is encryption capable, or is the // Check that the user is encryption capable, or is the
// public system user 'ownCloud' (for public shares) // public system user 'ownCloud' (for public shares)
if ( if (
$util->ready() $user == $this->publicShareKeyId
or $user == 'owncloud' or $user == $this->recoveryKeyId
or $util->ready()
) { ) {
// Construct array of ready UIDs for Keymanager{} // Construct array of ready UIDs for Keymanager{}
@ -738,25 +904,27 @@ class Util {
* @brief Encrypt keyfile to multiple users * @brief Encrypt keyfile to multiple users
* @param array $users list of users which should be able to access the file * @param array $users list of users which should be able to access the file
* @param string $filePath path of the file to be shared * @param string $filePath path of the file to be shared
* @return bool
*/ */
public function setSharedFileKeyfiles( Session $session, array $users, $filePath ) { public function setSharedFileKeyfiles( Session $session, array $users, $filePath ) {
// Make sure users are capable of sharing // Make sure users are capable of sharing
$filteredUids = $this->filterShareReadyUsers( $users ); $filteredUids = $this->filterShareReadyUsers( $users );
// trigger_error( print_r($filteredUids, 1) ); // If we're attempting to share to unready users
if ( ! empty( $filteredUids['unready'] ) ) { if ( ! empty( $filteredUids['unready'] ) ) {
// Notify user of unready userDir \OC_Log::write( 'Encryption library', 'Sharing to these user(s) failed as they are unready for encryption:"'.print_r( $filteredUids['unready'], 1 ), \OC_Log::WARN );
// TODO: Move this out of here; it belongs somewhere else
\OCP\JSON::error(); return false;
} }
// Get public keys for each user, ready for generating sharekeys // Get public keys for each user, ready for generating sharekeys
$userPubKeys = Keymanager::getPublicKeys( $this->view, $filteredUids['ready'] ); $userPubKeys = Keymanager::getPublicKeys( $this->view, $filteredUids['ready'] );
// Note proxy status then disable it
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Get the current users's private key for decrypting existing keyfile // Get the current users's private key for decrypting existing keyfile
@ -772,21 +940,19 @@ class Util {
// Save the recrypted key to it's owner's keyfiles directory // Save the recrypted key to it's owner's keyfiles directory
// Save new sharekeys to all necessary user directory // Save new sharekeys to all necessary user directory
// TODO: Reuse the keyfile, it it exists, instead of making a new one
if ( if (
! Keymanager::setFileKey( $this->view, $filePath, $fileOwner, $multiEncKey['data'] ) ! Keymanager::setFileKey( $this->view, $filePath, $fileOwner, $multiEncKey['data'] )
|| ! Keymanager::setShareKeys( $this->view, $filePath, $multiEncKey['keys'] ) || ! Keymanager::setShareKeys( $this->view, $filePath, $multiEncKey['keys'] )
) { ) {
trigger_error( "SET Share keys failed" ); \OC_Log::write( 'Encryption library', 'Keyfiles could not be saved for users sharing ' . $filePath, \OC_Log::ERROR );
return false;
} }
// Delete existing keyfile // Return proxy to original status
// Do this last to ensure file is recoverable in case of error \OC_FileProxy::$enabled = $proxyStatus;
// Keymanager::deleteFileKey( $this->view, $this->userId, $params['fileTarget'] );
\OC_FileProxy::$enabled = true;
return true; return true;
} }
@ -798,7 +964,18 @@ class Util {
public function getSharingUsersArray( $sharingEnabled, $filePath, $currentUserId = false ) { public function getSharingUsersArray( $sharingEnabled, $filePath, $currentUserId = false ) {
// Check if key recovery is enabled // Check if key recovery is enabled
$recoveryEnabled = $this->recoveryEnabled(); if (
\OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' )
&& $this->recoveryEnabledForUser()
) {
$recoveryEnabled = true;
} else {
$recoveryEnabled = false;
}
// Make sure that a share key is generated for the owner too // Make sure that a share key is generated for the owner too
list( $owner, $ownerPath ) = $this->getUidAndFilename( $filePath ); list( $owner, $ownerPath ) = $this->getUidAndFilename( $filePath );
@ -806,7 +983,11 @@ class Util {
if ( $sharingEnabled ) { if ( $sharingEnabled ) {
// Find out who, if anyone, is sharing the file // Find out who, if anyone, is sharing the file
$userIds = \OCP\Share::getUsersSharingFile( $ownerPath, $owner,true, true, true ); $result = \OCP\Share::getUsersSharingFile( $ownerPath, $owner,true, true, true );
$userIds = $result['users'];
if ( $result['public'] ) {
$userIds[] = $this->publicShareKeyId;
}
} }
@ -814,16 +995,19 @@ class Util {
// Admin UID to list of users to share to // Admin UID to list of users to share to
if ( $recoveryEnabled ) { if ( $recoveryEnabled ) {
// FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB? // Find recoveryAdmin user ID
$adminUid = 'recoveryAdmin'; $recoveryKeyId = \OC_Appconfig::getValue( 'files_encryption', 'recoveryKeyId' );
$userIds[] = $adminUid; // Add recoveryAdmin to list of users sharing
$userIds[] = $recoveryKeyId;
} }
// add current user if given // add current user if given
if ( $currentUserId != false ) { if ( $currentUserId != false ) {
$userIds[] = $currentUserId; $userIds[] = $currentUserId;
} }
// Remove duplicate UIDs // Remove duplicate UIDs
@ -833,6 +1017,78 @@ class Util {
} }
/**
* @brief Set file migration status for user
* @return bool
*/
public function setMigrationStatus( $status ) {
$sql = 'UPDATE
*PREFIX*encryption
SET
migrationStatus = ?
WHERE
uid = ?';
$args = array( $status, $this->userId );
$query = \OCP\DB::prepare( $sql );
if ( $query->execute( $args ) ) {
return true;
} else {
return false;
}
}
/**
* @brief Check whether pwd recovery is enabled for a given user
* @return 1 = yes, 0 = no, false = no record
* @note If records are not being returned, check for a hidden space
* at the start of the uid in db
*/
public function getMigrationStatus() {
$sql = 'SELECT
migrationStatus
FROM
`*PREFIX*encryption`
WHERE
uid = ?';
$args = array( $this->userId );
$query = \OCP\DB::prepare( $sql );
$result = $query->execute( $args );
$migrationStatus = array();
while( $row = $result->fetchRow() ) {
$migrationStatus[] = $row['migrationStatus'];
}
// If no record is found
if ( empty( $migrationStatus ) ) {
return false;
// If a record is found
} else {
return $migrationStatus[0];
}
}
/** /**
* @brief get uid of the owners of the file and the path to the file * @brief get uid of the owners of the file and the path to the file
* @param $path Path of the file to check * @param $path Path of the file to check
@ -842,13 +1098,20 @@ class Util {
*/ */
public function getUidAndFilename( $path ) { public function getUidAndFilename( $path ) {
$fileOwnerUid = \OC\Files\Filesystem::getOwner( $path ); $view = new \OC\Files\View($this->userFilesDir);
$fileOwnerUid = $view->getOwner( $path );
// handle public access
if($fileOwnerUid === false && $this->isPublic) {
$filename = $path;
$fileOwnerUid = $GLOBALS['fileOwner'];
return array ( $fileOwnerUid, $filename );
} else {
// Check that UID is valid // Check that UID is valid
if ( ! \OCP\User::userExists( $fileOwnerUid ) ) { if ( ! \OCP\User::userExists( $fileOwnerUid ) ) {
throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $path . '"' ); throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $path . '"' );
} }
// NOTE: Bah, this dependency should be elsewhere // NOTE: Bah, this dependency should be elsewhere
@ -862,7 +1125,7 @@ class Util {
} else { } else {
$info = \OC\Files\Filesystem::getFileInfo( $path ); $info = $view->getFileInfo( $path );
$ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' ); $ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' );
// Fetch real file path from DB // Fetch real file path from DB
@ -871,7 +1134,7 @@ class Util {
} }
// Make path relative for use by $view // Make path relative for use by $view
$relpath = $fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename; $relpath = \OC\Files\Filesystem::normalizePath($fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename);
// Check that the filename we're using is working // Check that the filename we're using is working
if ( $this->view->file_exists( $relpath ) ) { if ( $this->view->file_exists( $relpath ) ) {
@ -883,6 +1146,8 @@ class Util {
return false; return false;
} }
}
} }
@ -892,18 +1157,129 @@ class Util {
* @return array with list of files relative to the users files folder * @return array with list of files relative to the users files folder
*/ */
public function getAllFiles( $dir ) { public function getAllFiles( $dir ) {
$result = array(); $result = array();
$content = $this->view->getDirectoryContent( $this->userFilesDir . $dir ); $content = $this->view->getDirectoryContent( $this->userFilesDir . $dir );
// handling for re shared folders
$path_split = explode( '/', $dir );
$shared = '';
if( $path_split[1] === 'Shared' ) {
$shared = '/Shared';
}
foreach ( $content as $c ) { foreach ( $content as $c ) {
if ($c['type'] === "dir" ) {
$result = array_merge($result, $this->getAllFiles(substr($c['path'],5))); $sharedPart = $path_split[sizeof( $path_split )-1];
$targetPathSplit = array_reverse( explode( '/', $c['path'] ) );
$path = '';
// rebuild path
foreach ( $targetPathSplit as $pathPart ) {
if ( $pathPart !== $sharedPart ) {
$path = '/' . $pathPart . $path;
} else { } else {
$result[] = substr($c['path'], 5);
} break;
}
return $result; }
}
$path = $dir.$path;
if ($c['type'] === "dir" ) {
$result = array_merge( $result, $this->getAllFiles( $path ) );
} else {
$result[] = $path;
}
}
return $result;
}
/**
* @brief get shares parent.
* @param int $id of the current share
* @return array of the parent
*/
public static function getShareParent( $id ) {
$query = \OC_DB::prepare( 'SELECT `file_target`, `item_type`'
.' FROM `*PREFIX*share`'
.' WHERE `id` = ?' );
$result = $query->execute( array( $id ) );
$row = $result->fetchRow();
return $row;
}
/**
* @brief get owner of the shared files.
* @param int $Id of a share
* @return owner
*/
public function getOwnerFromSharedFile( $id ) {
$query = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 );
$source = $query->execute( array( $id ) )->fetchRow();
if ( isset($source['parent'] ) ) {
$parent = $source['parent'];
while ( isset( $parent ) ) {
$query = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 );
$item = $query->execute( array( $parent ) )->fetchRow();
if ( isset( $item['parent'] ) ) {
$parent = $item['parent'];
} else {
$fileOwner = $item['uid_owner'];
break;
}
}
} else {
$fileOwner = $source['uid_owner'];
}
return $fileOwner;
}
public function getUserId()
{
return $this->userId;
}
public function getUserFilesDir()
{
return $this->userFilesDir;
} }
} }

View File

@ -8,20 +8,22 @@
\OC_Util::checkAdminUser(); \OC_Util::checkAdminUser();
$tmpl = new OCP\Template( 'files_encryption', 'settings' ); $tmpl = new OCP\Template( 'files_encryption', 'settings-admin' );
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) ); $blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
// Check if an adminRecovery account is enabled for recovering files after lost pwd // Check if an adminRecovery account is enabled for recovering files after lost pwd
$view = new OC_FilesystemView( '' ); $view = new OC_FilesystemView( '' );
$util = new \OCA\Encryption\Util( $view, \OCP\USER::getUser() );
$recoveryEnabled = $util->recoveryEnabled(); $recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
$recoveryAdminUid = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminUid' );
$tmpl->assign( 'blacklist', $blackList ); $tmpl->assign( 'blacklist', $blackList );
$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) ); $tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) );
$tmpl->assign( 'recoveryEnabled', $recoveryEnabled ); $tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
$tmpl->assign( 'recoveryAdminUid', $recoveryAdminUid );
\OCP\Util::addscript( 'files_encryption', 'settings' ); \OCP\Util::addscript( 'files_encryption', 'settings-admin' );
\OCP\Util::addscript( 'core', 'multiselect' ); \OCP\Util::addscript( 'core', 'multiselect' );
return $tmpl->fetchPage(); return $tmpl->fetchPage();

View File

@ -6,10 +6,35 @@
* See the COPYING-README file. * See the COPYING-README file.
*/ */
// Add CSS stylesheet
\OC_Util::addStyle( 'files_encryption', 'settings-personal' );
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal'); $tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) ); $blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
// Add human readable message in case nothing is blacklisted
if (
1 == count( $blackList )
&& $blackList[0] == ''
) {
// FIXME: Make this string translatable
$blackList[0] = "(None - all filetypes will be encrypted)";
}
$user = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new \OCA\Encryption\Util( $view, $user );
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
\OCP\Util::addscript( 'files_encryption', 'settings-personal' );
$tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
$tmpl->assign( 'recoveryEnabledForUser', $recoveryEnabledForUser );
$tmpl->assign( 'blacklist', $blackList ); $tmpl->assign( 'blacklist', $blackList );
return $tmpl->fetchPage(); return $tmpl->fetchPage();

View File

@ -4,22 +4,16 @@
<p> <p>
<strong><?php p($l->t( 'Encryption' )); ?></strong> <strong><?php p($l->t( 'Encryption' )); ?></strong>
<br /> <br />
<?php p($l->t( "Exclude the following file types from encryption:" )); ?>
<br />
<select
id='encryption_blacklist'
title="<?php p($l->t( 'None' ))?>"
multiple="multiple">
<?php foreach($_["blacklist"] as $type): ?>
<option selected="selected" value="<?php p($type); ?>"> <?php p($type); ?> </option>
<?php endforeach;?>
</select>
</p> </p>
<p> <p>
<?php p($l->t( "Enable encryption passwords recovery account (allow sharing to recovery account):" )); ?> <?php p($l->t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?>
<br /> <br />
<br />
<?php if ( empty( $_['recoveryAdminUid'] ) ): ?>
<input type="password" name="recoveryPassword" id="recoveryPassword" />
<label for="recoveryPassword">Recovery account password</label>
<br />
<?php endif; ?>
<input <input
type='radio' type='radio'
name='adminEnableRecovery' name='adminEnableRecovery'

View File

@ -3,13 +3,17 @@
<legend> <legend>
<?php p( $l->t( 'Encryption' ) ); ?> <?php p( $l->t( 'Encryption' ) ); ?>
</legend> </legend>
<p> <p>
<?php p($l->t( 'File encryption is enabled.' )); ?> <!-- <?php p( $l->t( 'File encryption is enabled.' ) ); ?> -->
</p> </p>
<?php if ( ! empty( $_["blacklist"] ) ): ?> <?php if ( ! empty( $_["blacklist"] ) ): ?>
<p> <p>
<strong>File types</strong>
<br />
<?php p( $l->t( 'The following file types will not be encrypted:' ) ); ?> <?php p( $l->t( 'The following file types will not be encrypted:' ) ); ?>
</p> </p>
<ul> <ul>
<?php foreach( $_["blacklist"] as $type ): ?> <?php foreach( $_["blacklist"] as $type ): ?>
<li> <li>
@ -18,5 +22,43 @@
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>
<?php endif; ?> <?php endif; ?>
<br />
<?php if ( $_["recoveryEnabled"] ): ?>
<p>
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery by sharing all files with administrator:" ) ); ?></label>
<br />
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?></em>
<br />
<input
type='radio'
name='userEnableRecovery'
value='1'
<?php echo ( $_["recoveryEnabledForUser"] == 1 ? 'checked="checked"' : '' ); ?> />
<?php p( $l->t( "Enabled" ) ); ?>
<br />
<input
type='radio'
name='userEnableRecovery'
value='0'
<?php echo ( $_["recoveryEnabledForUser"] == 0 ? 'checked="checked"' : '' ); ?> />
<?php p( $l->t( "Disabled" ) ); ?>
<div id="recoveryEnabledSuccess"><?php p( $l->t( 'File recovery settings updated' ) ); ?></div>
<div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
</p>
<?php endif; ?>
<br />
<p>
<label for="encryptAll"><?php p( $l->t( "Scan for unencrypted files and encrypt them" ) ); ?></label>
<br />
<em><?php p( $l->t( "Use this if you suspect that you still have files which are unencrypted, or encrypted using ownCloud 4 or older." ) ); ?></em>
<br />
<input type="submit" id="encryptAll" name="encryptAll" value="<?php p( $l->t( 'Scan and encrypt files' ) ); ?>" />
<input type="password" name="userPassword" id="userPassword" />
<label for="encryptAll"><?php p( $l->t( "Account password" ) ); ?></label>
<div id="encryptAllSuccess"><?php p( $l->t( 'Scan complete' ) );?></div>
<div id="encryptAllError"><?php p( $l->t( 'Unable to scan and encrypt files' ) );?></div>
</p>
</fieldset> </fieldset>
</form> </form>

View File

@ -15,13 +15,13 @@ require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
require_once realpath( dirname(__FILE__).'/../lib/util.php' ); require_once realpath( dirname(__FILE__).'/../lib/util.php' );
require_once realpath( dirname(__FILE__).'/../lib/helper.php' );
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
use OCA\Encryption; use OCA\Encryption;
// This has to go here because otherwise session errors arise, and the private // This has to go here because otherwise session errors arise, and the private
// encryption key needs to be saved in the session // encryption key needs to be saved in the session
\OC_User::login( 'admin', 'admin' );
/** /**
* @note It would be better to use Mockery here for mocking out the session * @note It would be better to use Mockery here for mocking out the session
@ -34,6 +34,9 @@ use OCA\Encryption;
class Test_Crypt extends \PHPUnit_Framework_TestCase { class Test_Crypt extends \PHPUnit_Framework_TestCase {
function setUp() { function setUp() {
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
// set content for encrypting / decrypting in tests // set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
@ -53,8 +56,23 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
$this->userId = 'admin'; $this->userId = 'admin';
$this->pass = 'admin'; $this->pass = 'admin';
\OC_Filesystem::init( '/' ); $userHome = \OC_User::getHome($this->userId);
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); $this->dataDir = str_replace('/'.$this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::setView(false);
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
} }
@ -222,38 +240,51 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
function testSymmetricStreamEncryptShortFileContent() { function testSymmetricStreamEncryptShortFileContent() {
$filename = 'tmp-'.time(); $filename = 'tmp-'.time().'.test';
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort ); $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
// Test that data was successfully written // Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) ); $this->assertTrue( is_int( $cryptedFile ) );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get file contents without using any wrapper to get it's actual contents on disk // Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename); $retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// Check that the file was encrypted before being written to disk // Check that the file was encrypted before being written to disk
$this->assertNotEquals( $this->dataShort, $retreivedCryptedFile ); $this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
// Get private key // Get the encrypted keyfile
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); $encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); // Attempt to fetch the user's shareKey
$shareKey = Encryption\Keymanager::getShareKey( $this->view, $this->userId, $filename );
// get session
$session = new Encryption\Session( $this->view );
// Get keyfile // get private key
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); $privateKey = $session->getPrivateKey( $this->userId );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Decrypt keyfile with shareKey
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
// Manually decrypt // Manually decrypt
$manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile ); $manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $retreivedCryptedFile, $plainKeyfile );
// Check that decrypted data matches // Check that decrypted data matches
$this->assertEquals( $this->dataShort, $manualDecrypt ); $this->assertEquals( $this->dataShort, $manualDecrypt );
// Teardown
$this->view->unlink( $this->userId . '/files/' . $filename );
Encryption\Keymanager::deleteFileKey( $this->view, $this->userId, $filename );
} }
/** /**
@ -265,7 +296,7 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
function testSymmetricStreamEncryptLongFileContent() { function testSymmetricStreamEncryptLongFileContent() {
// Generate a a random filename // Generate a a random filename
$filename = 'tmp-'.time(); $filename = 'tmp-'.time().'.test';
// Save long data as encrypted file using stream wrapper // Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong ); $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
@ -273,10 +304,16 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written // Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) ); $this->assertTrue( is_int( $cryptedFile ) );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get file contents without using any wrapper to get it's actual contents on disk // Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename); $retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n"; // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// Check that the file was encrypted before being written to disk // Check that the file was encrypted before being written to disk
$this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile ); $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
@ -291,18 +328,20 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
//print_r($e); //print_r($e);
// Get the encrypted keyfile
$encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
// Get private key // Attempt to fetch the user's shareKey
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); $shareKey = Encryption\Keymanager::getShareKey( $this->view, $this->userId, $filename );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); // get session
$session = new Encryption\Session( $this->view );
// get private key
$privateKey = $session->getPrivateKey( $this->userId );
// Get keyfile // Decrypt keyfile with shareKey
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); $plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Set var for reassembling decrypted content // Set var for reassembling decrypted content
$decrypt = ''; $decrypt = '';
@ -310,26 +349,20 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Manually decrypt chunk // Manually decrypt chunk
foreach ($e as $e) { foreach ($e as $e) {
// echo "\n\$e = $e"; $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $plainKeyfile );
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile );
// Assemble decrypted chunks // Assemble decrypted chunks
$decrypt .= $chunkDecrypt; $decrypt .= $chunkDecrypt;
// echo "\n\$chunkDecrypt = $chunkDecrypt";
} }
// echo "\n\$decrypt = $decrypt";
$this->assertEquals( $this->dataLong.$this->dataLong, $decrypt ); $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
// Teardown // Teardown
$this->view->unlink( $filename ); $this->view->unlink( $this->userId . '/files/' . $filename );
Encryption\Keymanager::deleteFileKey( $filename ); Encryption\Keymanager::deleteFileKey( $this->view, $this->userId, $filename );
} }
@ -346,14 +379,13 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written // Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) ); $this->assertTrue( is_int( $cryptedFile ) );
// Get file decrypted contents
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
$decrypt = file_get_contents( 'crypt://' . $filename ); $decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataShort, $decrypt ); $this->assertEquals( $this->dataShort, $decrypt );
// tear down
$this->view->unlink( $this->userId . '/files/' . $filename );
} }
function testSymmetricStreamDecryptLongFileContent() { function testSymmetricStreamDecryptLongFileContent() {
@ -366,14 +398,13 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written // Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) ); $this->assertTrue( is_int( $cryptedFile ) );
// Get file decrypted contents
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
$decrypt = file_get_contents( 'crypt://' . $filename ); $decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataLong, $decrypt ); $this->assertEquals( $this->dataLong, $decrypt );
// tear down
$this->view->unlink( $this->userId . '/files/' . $filename );
} }
// Is this test still necessary? // Is this test still necessary?
@ -600,6 +631,65 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
} }
function testRenameFile() {
$filename = 'tmp-'.time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file decrypted contents
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataLong, $decrypt );
$newFilename = 'tmp-new-'.time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->rename( $filename, $newFilename );
// Get file decrypted contents
$newDecrypt = file_get_contents( 'crypt://' . $newFilename );
$this->assertEquals( $this->dataLong, $newDecrypt );
// tear down
$view->unlink( $newFilename );
}
function testMoveFileIntoFolder() {
$filename = 'tmp-'.time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file decrypted contents
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataLong, $decrypt );
$newFolder = '/newfolder1';
$newFilename = 'tmp-new-'.time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->mkdir($newFolder);
$view->rename( $filename, $newFolder . '/' . $newFilename );
// Get file decrypted contents
$newDecrypt = file_get_contents( 'crypt://' . $newFolder . '/' . $newFilename );
$this->assertEquals( $this->dataLong, $newDecrypt );
// tear down
$view->unlink( $newFolder . '/' . $newFilename );
$view->unlink( $newFolder );
}
// function testEncryption(){ // function testEncryption(){
// //
// $key=uniqid(); // $key=uniqid();

View File

@ -13,17 +13,21 @@ require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
require_once realpath( dirname(__FILE__).'/../lib/util.php' ); require_once realpath( dirname(__FILE__).'/../lib/util.php' );
require_once realpath( dirname(__FILE__).'/../lib/helper.php' );
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
use OCA\Encryption; use OCA\Encryption;
// This has to go here because otherwise session errors arise, and the private // This has to go here because otherwise session errors arise, and the private
// encryption key needs to be saved in the session // encryption key needs to be saved in the session
\OC_User::login( 'admin', 'admin' ); //\OC_User::login( 'admin', 'admin' );
class Test_Keymanager extends \PHPUnit_Framework_TestCase { class Test_Keymanager extends \PHPUnit_Framework_TestCase {
function setUp() { function setUp() {
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
@ -45,9 +49,23 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$this->userId = 'admin'; $this->userId = 'admin';
$this->pass = 'admin'; $this->pass = 'admin';
\OC_Filesystem::init( '/' ); $userHome = \OC_User::getHome($this->userId);
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); $this->dataDir = str_replace('/'.$this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::setView(false);
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
} }
function tearDown(){ function tearDown(){
@ -60,8 +78,12 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->pass);
// Will this length vary? Perhaps we should use a range instead // Will this length vary? Perhaps we should use a range instead
$this->assertEquals( 2296, strlen( $key ) ); $this->assertGreaterThan( 27, strlen( $privateKey ) );
$this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $privateKey, 0, 27 ) );
} }
@ -69,7 +91,7 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId ); $key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId );
$this->assertEquals( 451, strlen( $key ) ); $this->assertGreaterThan( 26, strlen( $key ) );
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) ); $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) );
} }
@ -81,11 +103,19 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' ); $key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' );
$path = 'unittest-'.time().'txt'; $file = 'unittest-'.time().'.txt';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' ); //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
Encryption\Keymanager::setFileKey( $this->view, $file, $this->userId, $key['key'] );
Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] );
} }
@ -109,9 +139,15 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId ); $keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId );
$this->assertEquals( 451, strlen( $keys['publicKey'] ) ); $this->assertGreaterThan( 26, strlen( $keys['publicKey'] ) );
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) ); $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) );
$this->assertEquals( 2296, strlen( $keys['privateKey'] ) );
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $keys['privateKey'], $this->pass);
$this->assertGreaterThan( 27, strlen( $keys['privateKey'] ) );
$this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $privateKey, 0, 27 ) );
} }

View File

@ -0,0 +1,473 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
require_once realpath(dirname(__FILE__) . '/../../../3rdparty/Crypt_Blowfish/Blowfish.php');
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../lib/helper.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
{
function setUp()
{
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
$this->dataShort = 'hats';
$this->view = new \OC_FilesystemView('/');
$userHome = \OC_User::getHome('admin');
$this->dataDir = str_replace('/admin', '', $userHome);
$this->folder1 = '/folder1';
$this->subfolder = '/subfolder1';
$this->subsubfolder = '/subsubfolder1';
$this->filename = 'share-tmp.test';
// enable resharing
\OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes');
// clear share hooks
\OC_Hook::clear('OCP\\Share');
\OC::registerShareHooks();
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
// Sharing related hooks
\OCA\Encryption\Helper::registerShareHooks();
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\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');
// create users
$this->loginHelper('user1', true);
$this->loginHelper('user2', true);
$this->loginHelper('user3', true);
}
function tearDown()
{
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
// cleanup users
\OC_User::deleteUser('user1');
\OC_User::deleteUser('user2');
\OC_User::deleteUser('user3');
}
function testShareFile($withTeardown = true)
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user1 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// login as user1
$this->loginHelper('user1');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared/' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as admin
$this->loginHelper('admin');
// unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
}
function testReShareFile($withTeardown = true)
{
$this->testShareFile(false);
// login as user1
$this->loginHelper('user1');
// get the file info
$fileInfo = $this->view->getFileInfo('/user1/files/Shared/' . $this->filename);
// share the file with user2
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user2 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// login as user2
$this->loginHelper('user2');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename);
// check if data is the same as previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as user1
$this->loginHelper('user1');
// unshare the file with user2
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// login as admin
$this->loginHelper('admin');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// unshare the file with user1
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
}
function testShareFolder($withTeardown = true)
{
// login as admin
$this->loginHelper('admin');
// 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
$cryptedFile = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created folder
$fileInfo = $this->view->getFileInfo('/admin/files' . $this->folder1);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the folder with user1
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user1 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// login as user1
$this->loginHelper('user1');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as admin
$this->loginHelper('admin');
// unshare the folder with user1
\OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
}
return $fileInfo;
}
function testReShareFolder($withTeardown = true)
{
$fileInfoFolder1 = $this->testShareFolder(false);
// login as user1
$this->loginHelper('user1');
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created folder
$fileInfoSubFolder = $this->view->getFileInfo('/user1/files/Shared' . $this->folder1 . $this->subfolder);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfoSubFolder));
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file with user2
\OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user2 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as user2
$this->loginHelper('user2');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// get the file info
$fileInfo = $this->view->getFileInfo('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if we have fileInfos
$this->assertTrue(is_array($fileInfo));
// share the file with user3
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user3 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user3
$this->loginHelper('user3');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user3/files/Shared/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as user2
$this->loginHelper('user2');
// unshare the file with user3
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user1
$this->loginHelper('user1');
// unshare the folder with user2
\OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as admin
$this->loginHelper('admin');
// unshare the folder1 with user1
\OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
}
}
function testPublicShareFile()
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, false);
// login as admin
$this->loginHelper('admin');
$publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
// check if share key for public exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// some hacking to simulate public link
$GLOBALS['app'] = 'files_sharing';
$GLOBALS['fileOwner'] = 'admin';
\OC_User::setUserId('');
// get file contents
$retrievedCryptedFile = file_get_contents('crypt://' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// tear down
// login as admin
$this->loginHelper('admin');
// unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
function loginHelper($user, $create = false)
{
if ($create) {
\OC_User::createUser($user, $user);
}
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::setView(false);
\OC_Util::setupFS($user);
\OC_User::setUserId($user);
$params['uid'] = $user;
$params['password'] = $user;
OCA\Encryption\Hooks::login($params);
}
}

View File

@ -1,4 +1,4 @@
// <?php <?php
// /** // /**
// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> // * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
// * This file is licensed under the Affero General Public License version 3 or // * This file is licensed under the Affero General Public License version 3 or

View File

@ -24,13 +24,15 @@ $loader->register();
use \Mockery as m; use \Mockery as m;
use OCA\Encryption; use OCA\Encryption;
\OC_User::login( 'admin', 'admin' );
class Test_Enc_Util extends \PHPUnit_Framework_TestCase { class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
function setUp() { function setUp() {
// reset backend
\OC_User::useBackend('database');
\OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' ); \OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
// set content for encrypting / decrypting in tests // set content for encrypting / decrypting in tests
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
@ -39,9 +41,6 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
$this->userId = 'admin';
$this->pass = 'admin';
$keypair = Encryption\Crypt::createKeypair(); $keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey']; $this->genPublicKey = $keypair['publicKey'];
@ -55,8 +54,26 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$this->view = new \OC_FilesystemView( '/' ); $this->view = new \OC_FilesystemView( '/' );
$this->mockView = m::mock('OC_FilesystemView'); $userHome = \OC_User::getHome($this->userId);
$this->util = new Encryption\Util( $this->mockView, $this->userId ); $this->dataDir = str_replace('/'.$this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::setView(false);
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
$mockView = m::mock('OC_FilesystemView');
$this->util = new Encryption\Util( $mockView, $this->userId );
} }
@ -68,6 +85,9 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
/** /**
* @brief test that paths set during User construction are correct * @brief test that paths set during User construction are correct
*
*
*
*/ */
function testKeyPaths() { function testKeyPaths() {
@ -90,8 +110,8 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$mockView = m::mock('OC_FilesystemView'); $mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false ); $mockView->shouldReceive( 'file_exists' )->times(7)->andReturn( false );
$mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true ); $mockView->shouldReceive( 'mkdir' )->times(6)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId ); $util = new Encryption\Util( $mockView, $this->userId );
@ -107,7 +127,7 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$mockView = m::mock('OC_FilesystemView'); $mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true ); $mockView->shouldReceive( 'file_exists' )->times(8)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId ); $util = new Encryption\Util( $mockView, $this->userId );
@ -141,7 +161,7 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$mockView = m::mock('OC_FilesystemView'); $mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true ); $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( true );
$util = new Encryption\Util( $mockView, $this->userId ); $util = new Encryption\Util( $mockView, $this->userId );
@ -158,32 +178,32 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$util = new Encryption\Util( $this->view, $this->userId ); $util = new Encryption\Util( $this->view, $this->userId );
$files = $util->findEncFiles( '/', 'encrypted' ); $files = $util->findEncFiles( '/'.$this->userId.'/');
var_dump( $files ); //var_dump( $files );
# TODO: Add more tests here to check that if any of the dirs are # TODO: Add more tests here to check that if any of the dirs are
# then false will be returned. Use strict ordering? # then false will be returned. Use strict ordering?
} }
function testRecoveryEnabled() { function testRecoveryEnabledForUser() {
$util = new Encryption\Util( $this->view, $this->userId ); $util = new Encryption\Util( $this->view, $this->userId );
// Record the value so we can return it to it's original state later // Record the value so we can return it to it's original state later
$enabled = $util->recoveryEnabled(); $enabled = $util->recoveryEnabledForUser();
$this->assertTrue( $util->setRecovery( 1 ) ); $this->assertTrue( $util->setRecoveryForUser( 1 ) );
$this->assertEquals( 1, $util->recoveryEnabled() ); $this->assertEquals( 1, $util->recoveryEnabledForUser() );
$this->assertTrue( $util->setRecovery( 0 ) ); $this->assertTrue( $util->setRecoveryForUser( 0 ) );
$this->assertEquals( 0, $util->recoveryEnabled() ); $this->assertEquals( 0, $util->recoveryEnabledForUser() );
// Return the setting to it's previous state // Return the setting to it's previous state
$this->assertTrue( $util->setRecovery( $enabled ) ); $this->assertTrue( $util->setRecoveryForUser( $enabled ) );
} }
@ -191,10 +211,24 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
\OC_User::setUserId( 'admin' ); \OC_User::setUserId( 'admin' );
$this->util->getUidAndFilename( 'test1.txt' ); $filename = 'tmp-'.time().'.test';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
$util = new Encryption\Util( $this->view, $this->userId );
list($fileOwnerUid, $file) = $util->getUidAndFilename( $filename );
$this->assertEquals('admin', $fileOwnerUid);
$this->assertEquals($file, $filename);
} }
// /** // /**

View File

@ -13,6 +13,5 @@
"{count} files" => "{count} файла", "{count} files" => "{count} файла",
"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!", "Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
"Restore" => "Възтановяване", "Restore" => "Възтановяване",
"Delete" => "Изтриване", "Delete" => "Изтриване"
"Deleted Files" => "Изтрити файлове"
); );

View File

@ -2,13 +2,13 @@
"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s", "Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s",
"Couldn't restore %s" => "Tidak dapat memulihkan %s", "Couldn't restore %s" => "Tidak dapat memulihkan %s",
"perform restore operation" => "jalankan operasi pemulihan", "perform restore operation" => "jalankan operasi pemulihan",
"Error" => "Galat", "Error" => "kesalahan",
"delete file permanently" => "hapus berkas secara permanen", "delete file permanently" => "hapus berkas secara permanen",
"Delete permanently" => "Hapus secara permanen", "Delete permanently" => "hapus secara permanen",
"Name" => "Nama", "Name" => "Nama",
"Deleted" => "Dihapus", "Deleted" => "Dihapus",
"1 folder" => "1 folder", "1 folder" => "1 map",
"{count} folders" => "{count} folder", "{count} folders" => "{count} map",
"1 file" => "1 berkas", "1 file" => "1 berkas",
"{count} files" => "{count} berkas", "{count} files" => "{count} berkas",
"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!", "Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!",

View File

@ -1,10 +1,5 @@
<?php $TRANSLATIONS = array( <?php $TRANSLATIONS = array(
"Error" => "Feil", "Error" => "Feil",
"Delete permanently" => "Slett for godt",
"Name" => "Namn", "Name" => "Namn",
"1 folder" => "1 mappe",
"{count} folders" => "{count} mapper",
"1 file" => "1 fil",
"{count} files" => "{count} filer",
"Delete" => "Slett" "Delete" => "Slett"
); );

View File

@ -8,9 +8,9 @@
"Name" => "Nazwa", "Name" => "Nazwa",
"Deleted" => "Usunięte", "Deleted" => "Usunięte",
"1 folder" => "1 folder", "1 folder" => "1 folder",
"{count} folders" => "Ilość folderów: {count}", "{count} folders" => "{count} foldery",
"1 file" => "1 plik", "1 file" => "1 plik",
"{count} files" => "Ilość plików: {count}", "{count} files" => "{count} pliki",
"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!", "Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!",
"Restore" => "Przywróć", "Restore" => "Przywróć",
"Delete" => "Usuń", "Delete" => "Usuń",

View File

@ -13,6 +13,6 @@
"{count} files" => "{count} ficheiros", "{count} files" => "{count} ficheiros",
"Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!", "Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!",
"Restore" => "Restaurar", "Restore" => "Restaurar",
"Delete" => "Eliminar", "Delete" => "Apagar",
"Deleted Files" => "Ficheiros Apagados" "Deleted Files" => "Ficheiros Apagados"
); );

View File

@ -1,6 +1,5 @@
<?php $TRANSLATIONS = array( <?php $TRANSLATIONS = array(
"Error" => "Eroare", "Error" => "Eroare",
"Delete permanently" => "Stergere permanenta",
"Name" => "Nume", "Name" => "Nume",
"1 folder" => "1 folder", "1 folder" => "1 folder",
"{count} folders" => "{count} foldare", "{count} folders" => "{count} foldare",

View File

@ -5,7 +5,7 @@
"Error" => "Chyba", "Error" => "Chyba",
"delete file permanently" => "trvalo zmazať súbor", "delete file permanently" => "trvalo zmazať súbor",
"Delete permanently" => "Zmazať trvalo", "Delete permanently" => "Zmazať trvalo",
"Name" => "Názov", "Name" => "Meno",
"Deleted" => "Zmazané", "Deleted" => "Zmazané",
"1 folder" => "1 priečinok", "1 folder" => "1 priečinok",
"{count} folders" => "{count} priečinkov", "{count} folders" => "{count} priečinkov",

View File

@ -29,6 +29,17 @@ class Trashbin {
// unit: percentage; 50% of available disk space/quota // unit: percentage; 50% of available disk space/quota
const DEFAULTMAXSIZE=50; const DEFAULTMAXSIZE=50;
public static function getUidAndFilename($filename) {
$uid = \OC\Files\Filesystem::getOwner($filename);
\OC\Files\Filesystem::initMountPoints($uid);
if ( $uid != \OCP\User::getUser() ) {
$info = \OC\Files\Filesystem::getFileInfo($filename);
$ownerView = new \OC\Files\View('/'.$uid.'/files');
$filename = $ownerView->getPath($info['fileid']);
}
return array($uid, $filename);
}
/** /**
* move file to the trash bin * move file to the trash bin
* *
@ -63,7 +74,11 @@ class Trashbin {
$trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
} }
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view); $sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view);
\OC_FileProxy::$enabled = $proxyStatus;
if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) { if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) {
$trashbinSize += $sizeOfAddedFiles; $trashbinSize += $sizeOfAddedFiles;
@ -110,13 +125,17 @@ class Trashbin {
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
if ($view->is_dir('files_versions/' . $file_path)) { $rootView = new \OC\Files\View('/');
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_versions/' . $file_path));
$view->rename('files_versions/' . $file_path, 'files_trashbin/versions/' . $filename . '.d' . $timestamp); list($owner, $ownerPath) = self::getUidAndFilename($file_path);
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path)) {
if ($rootView->is_dir($owner.'/files_versions/' . $ownerPath)) {
$size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath));
$rootView->rename($owner.'/files_versions/' . $ownerPath, $user.'/files_trashbin/versions/' . $filename . '.d' . $timestamp);
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
foreach ($versions as $v) { foreach ($versions as $v) {
$size += $view->filesize('files_versions' . $v['path'] . '.v' . $v['version']); $size += $rootView->filesize($owner.'/files_versions' . $v['path'] . '.v' . $v['version']);
$view->rename('files_versions' . $v['path'] . '.v' . $v['version'], 'files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp); $rootView->rename($owner.'/files_versions' . $v['path'] . '.v' . $v['version'], $user.'/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
} }
} }
@ -143,35 +162,38 @@ class Trashbin {
if (\OCP\App::isEnabled('files_encryption')) { if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
// disable proxy to prevent recursive calls // disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// retain key files // retain key files
$keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/' . $file_path); $keyfile = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/keyfiles/' . $ownerPath);
if ($view->is_dir($keyfile) || $view->file_exists($keyfile . '.key')) { if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) {
$user = \OCP\User::getUser();
// move keyfiles // move keyfiles
if ($view->is_dir($keyfile)) { if ($rootView->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); $size += self::calculateSize(new \OC\Files\View($keyfile));
$view->rename($keyfile, 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp); $rootView->rename($keyfile, $user.'/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
} else { } else {
$size += $view->filesize($keyfile . '.key'); $size += $rootView->filesize($keyfile . '.key');
$view->rename($keyfile . '.key', 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp); $rootView->rename($keyfile . '.key', $user.'/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
} }
} }
// retain share keys // retain share keys
$sharekeys = \OC\Files\Filesystem::normalizePath('files_encryption/share-keys/' . $file_path); $sharekeys = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/share-keys/' . $ownerPath);
if ($view->is_dir($sharekeys)) { if ($rootView->is_dir($sharekeys)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys)); $size += self::calculateSize(new \OC\Files\View($sharekeys));
$view->rename($sharekeys, 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp); $rootView->rename($sharekeys, $user.'/files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
} else { } else {
// get local path to share-keys // get local path to share-keys
$localShareKeysPath = $view->getLocalFile($sharekeys); $localShareKeysPath = $rootView->getLocalFile($sharekeys);
// handle share-keys // handle share-keys
$matches = glob(preg_quote($localShareKeysPath).'*.shareKey'); $matches = glob(preg_quote($localShareKeysPath).'*.shareKey');
@ -186,10 +208,10 @@ class Trashbin {
if($pathinfo['basename'] == $ownerShareKey) { if($pathinfo['basename'] == $ownerShareKey) {
// calculate size // calculate size
$size += $view->filesize($sharekeys. '.' . $user. '.shareKey'); $size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey');
// move file // move file
$view->rename($sharekeys. '.' . $user. '.shareKey', 'files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp); $rootView->rename($sharekeys. '.' . $user. '.shareKey', $user.'/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp);
} else { } else {
// calculate size // calculate size
@ -321,6 +343,12 @@ class Trashbin {
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
list($owner, $ownerPath) = self::getUidAndFilename($target);
if ($timestamp) { if ($timestamp) {
$versionedFile = $filename; $versionedFile = $filename;
} else { } else {
@ -329,15 +357,15 @@ class Trashbin {
if ($view->is_dir('/files_trashbin/versions/'.$file)) { if ($view->is_dir('/files_trashbin/versions/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file)); $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file));
$view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath('files_versions/' . $location . '/' . $filename . $ext)); $rootView->rename(\OC\Files\Filesystem::normalizePath($user.'/files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath($owner.'/files_versions/' . $ownerPath));
} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) { } else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) {
foreach ($versions as $v) { foreach ($versions as $v) {
if ($timestamp) { if ($timestamp) {
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp); $size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp);
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v); $rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
} else { } else {
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v); $size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v);
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v); $rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
} }
} }
} }
@ -356,7 +384,7 @@ class Trashbin {
* @param $file complete path to file * @param $file complete path to file
* @param $filename name of file * @param $filename name of file
* @param $ext file extension in case a file with the same $filename already exists * @param $ext file extension in case a file with the same $filename already exists
* @param $location location if file * @param $location location of file
* @param $timestamp deleteion time * @param $timestamp deleteion time
* *
* @return size of restored encrypted file * @return size of restored encrypted file
@ -366,20 +394,25 @@ class Trashbin {
$size = 0; $size = 0;
if (\OCP\App::isEnabled('files_encryption')) { if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
list($owner, $ownerPath) = self::getUidAndFilename($target);
$path_parts = pathinfo($file); $path_parts = pathinfo($file);
$source_location = $path_parts['dirname']; $source_location = $path_parts['dirname'];
if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) { if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) {
if($source_location != '.') { if($source_location != '.') {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename); $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename); $sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename);
} else { } else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename); $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename); $sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $filename);
} }
} else { } else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key'); $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key');
} }
if ($timestamp) { if ($timestamp) {
@ -390,35 +423,36 @@ class Trashbin {
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ($view->file_exists($keyfile)) { if ($rootView->file_exists($keyfile)) {
// handle directory // handle directory
if ($view->is_dir($keyfile)) { if ($rootView->is_dir($keyfile)) {
// handle keyfiles // handle keyfiles
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); $size += self::calculateSize(new \OC\Files\View($keyfile));
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext); $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath);
// handle share-keys // handle share-keys
if ($timestamp) { if ($timestamp) {
$sharekey .= '.d' . $timestamp; $sharekey .= '.d' . $timestamp;
} }
$view->rename($sharekey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext); $size += self::calculateSize(new \OC\Files\View($sharekey));
$rootView->rename($sharekey, $owner.'/files_encryption/share-keys/' . $ownerPath);
} else { } else {
// handle keyfiles // handle keyfiles
$size += $view->filesize($keyfile); $size += $rootView->filesize($keyfile);
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key'); $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key');
// handle share-keys // handle share-keys
$ownerShareKey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey'); $ownerShareKey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey');
if ($timestamp) { if ($timestamp) {
$ownerShareKey .= '.d' . $timestamp; $ownerShareKey .= '.d' . $timestamp;
} }
$size += $view->filesize($ownerShareKey); $size += $rootView->filesize($ownerShareKey);
// move only owners key // move only owners key
$view->rename($ownerShareKey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext . '.' . $user. '.shareKey'); $rootView->rename($ownerShareKey, $owner.'/files_encryption/share-keys/' . $ownerPath . '.' . $user. '.shareKey');
// try to re-share if file is shared // try to re-share if file is shared
$filesystemView = new \OC_FilesystemView('/'); $filesystemView = new \OC_FilesystemView('/');
@ -426,7 +460,7 @@ class Trashbin {
$util = new \OCA\Encryption\Util($filesystemView, $user); $util = new \OCA\Encryption\Util($filesystemView, $user);
// fix the file size // fix the file size
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $user . '/files/'. $location. '/' .$filename); $absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath);
$util->fixFileSize($absolutePath); $util->fixFileSize($absolutePath);
// get current sharing state // get current sharing state
@ -475,7 +509,25 @@ class Trashbin {
$file = $filename; $file = $filename;
} }
$size += self::deleteVersions($view, $file, $filename, $timestamp);
$size += self::deleteEncryptionKeys($view, $file, $filename, $timestamp);
if ($view->is_dir('/files_trashbin/files/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
} else {
$size += $view->filesize('/files_trashbin/files/'.$file);
}
$view->unlink('/files_trashbin/files/'.$file);
$trashbinSize -= $size;
self::setTrashbinSize($user, $trashbinSize);
return $size;
}
private static function deleteVersions($view, $file, $filename, $timestamp) {
$size = 0;
if ( \OCP\App::isEnabled('files_versions') ) { if ( \OCP\App::isEnabled('files_versions') ) {
$user = \OCP\User::getUser();
if ($view->is_dir('files_trashbin/versions/'.$file)) { if ($view->is_dir('files_trashbin/versions/'.$file)) {
$size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file)); $size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
$view->unlink('files_trashbin/versions/'.$file); $view->unlink('files_trashbin/versions/'.$file);
@ -491,35 +543,37 @@ class Trashbin {
} }
} }
} }
return $size;
}
private static function deleteEncryptionKeys($view, $file, $filename, $timestamp) {
$size = 0;
if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser();
// Take care of encryption keys
$parts = pathinfo($file);
if ($view->is_dir('/files_trashbin/files/' . $file)) { if ($view->is_dir('/files_trashbin/files/' . $file)) {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename); $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
} else { } else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key'); $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key');
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename . '.' . $user . '.shareKey');
} }
if ($timestamp) { if ($timestamp) {
$keyfile .= '.d' . $timestamp; $keyfile .= '.d' . $timestamp;
$sharekeys .= '.d' . $timestamp;
} }
if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) { if ($view->file_exists($keyfile)) {
if ($view->is_dir($keyfile)) { if ($view->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
} else { } else {
$size += $view->filesize($keyfile); $size += $view->filesize($keyfile);
$size += $view->filesize($sharekeys);
} }
$view->unlink($keyfile); $view->unlink($keyfile);
$view->unlink($sharekeys);
} }
if ($view->is_dir('/files_trashbin/files/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
} else {
$size += $view->filesize('/files_trashbin/files/'.$file);
} }
$view->unlink('/files_trashbin/files/'.$file);
$trashbinSize -= $size;
self::setTrashbinSize($user, $trashbinSize);
return $size; return $size;
} }

View File

@ -8,6 +8,7 @@
require_once __DIR__.'/../lib/base.php'; require_once __DIR__.'/../lib/base.php';
OC_App::enable('files_encryption');
OC_App::enable('calendar'); OC_App::enable('calendar');
OC_App::enable('contacts'); OC_App::enable('contacts');
OC_App::enable('apptemplateadvanced'); OC_App::enable('apptemplateadvanced');