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
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* 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
*/
use OCA\Encryption;
\OCP\JSON::checkAdminUser();
\OCP\JSON::checkAppEnabled( 'files_encryption' );
\OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck();
$return = $doSetup = false;
$return = false;
if (
isset( $_POST['adminEnableRecovery'] )
&& $_POST['adminEnableRecovery'] == 1
&& isset( $_POST['recoveryPassword'] )
&& ! empty ( $_POST['recoveryPassword'] )
// Enable recoveryAdmin
if (
isset($_POST['adminEnableRecovery'])
&& 1 == $_POST['adminEnableRecovery']
) {
// TODO: Let the admin set this themselves
$recoveryAdminUid = 'recoveryAdmin';
// If desired recoveryAdmin UID is already in use
if ( ! \OC_User::userExists( $recoveryAdminUid ) ) {
// Create new recoveryAdmin user
\OC_User::createUser( $recoveryAdminUid, $_POST['recoveryPassword'] );
$doSetup = true;
} else {
// Get list of admin users
$admins = OC_Group::usersInGroup( 'admin' );
// If the existing recoveryAdmin UID is an admin
if ( in_array( $recoveryAdminUid, $admins ) ) {
// The desired recoveryAdmi UID pre-exists and can be used
$doSetup = true;
// If the recoveryAdmin UID exists but doesn't have admin rights
} else {
$return = false;
$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'])
&& !empty($_POST['recoveryPassword'])
) {
$keypair = \OCA\Encryption\Crypt::createKeypair();
\OC_FileProxy::$enabled = false;
// Save public key
if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
$view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
// Encrypt private key empthy passphrase
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $_POST['recoveryPassword']);
// Save private key
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
\OC_FileProxy::$enabled = true;
}
// 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;
}
// Set recoveryAdmin as enabled
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
$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) ? OC_JSON::success() : OC_JSON::error();
// Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\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
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
@ -17,26 +15,21 @@ use OCA\Encryption;
if (
isset( $_POST['userEnableRecovery'] )
&& ( 0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'] )
) {
// Ensure preference is an integer
$recoveryEnabled = intval( $_POST['userEnableRecovery'] );
$userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $userId );
$util = new \OCA\Encryption\Util( $view, $userId );
// Save recovery preference to DB
$result = $util->setRecovery( $recoveryEnabled );
$return = $util->setRecoveryForUser( $_POST['userEnableRecovery'] );
if ( $result ) {
} else {
$return = false;
\OCP\JSON::success();
} else {
\OCP\JSON::error();
}
}
}
// 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\Session'] = 'files_encryption/lib/session.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() );
// User-related hooks
OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
// User related hooks
OCA\Encryption\Helper::registerUserHooks();
// Sharing-related hooks
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' );
// Sharing related hooks
OCA\Encryption\Helper::registerShareHooks();
// Webdav-related hooks
OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfileFromClient' );
// Webdav related hooks
OCA\Encryption\Helper::registerWebdavHooks();
// Filesystem related hooks
OCA\Encryption\Helper::registerFilesystemHooks();
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
$view = new OC_FilesystemView( '/' );
$view = new \OC\Files\View( '/' );
$session = new OCA\Encryption\Session( $view );
@ -47,5 +48,6 @@ if (
}
// Register settings scripts
OCP\App::registerAdmin( 'files_encryption', 'settings' );
OCP\App::registerAdmin( 'files_encryption', 'settings-admin' );
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );

View File

@ -27,6 +27,13 @@
<default>0</default>
<comments>Whether encryption key recovery is enabled</comments>
</field>
<field>
<name>migrationStatus</name>
<type>boolean</type>
<notnull>true</notnull>
<default>0</default>
<comments>Whether encryption migration has been performed</comments>
</field>
</declaration>
</table>
</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;
use OC\Files\Filesystem;
/**
* Class for hook specific logic
*/
class Hooks {
// TODO: use passphrase for encrypting private key that is separate to
@ -46,15 +47,11 @@ class Hooks {
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $params['uid'] );
// Check files_encryption infrastructure is ready for action
if ( ! $util->ready() ) {
\OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
return $util->setupServerSide( $params['password'] );
}
// setup user, if user not ready force relogin
if(Helper::setupUser($util, $params['password']) === false) {
return false;
}
\OC_FileProxy::$enabled = false;
@ -67,49 +64,89 @@ class Hooks {
$session = new Session( $view );
$session->setPrivateKey( $privateKey, $params['uid'] );
//FIXME: disabled because it gets called each time a user do an operation on iPhone
//FIXME: we need a better place doing this and maybe only one time or by user
/*$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
// Set legacy encryption key if it exists, to support
// depreciated encryption system
if (
$view1->file_exists( 'encryption.key' )
&& $encLegacyKey = $view1->file_get_contents( 'encryption.key' )
) {
// Check if first-run file migration has already been performed
$migrationCompleted = $util->getMigrationStatus();
$plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
// If migration not yet done
if ( ! $migrationCompleted ) {
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
$session->setLegacyKey( $plainLegacyKey );
// Set legacy encryption key if it exists, to support
// depreciated encryption system
if (
$view1->file_exists( 'encryption.key' )
&& $encLegacyKey = $view1->file_get_contents( 'encryption.key' )
) {
$plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
$session->setLegacyKey( $plainLegacyKey );
}
\OC_FileProxy::$enabled = false;
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
\OC_FileProxy::$enabled = false;
// Encrypt existing user files:
// This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt)
if (
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO
);
}
// Register successful migration in DB
$util->setMigrationStatus( 1 );
}
\OC_FileProxy::$enabled = false;
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
\OC_FileProxy::$enabled = false;*/
// Encrypt existing user files:
// This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt)
/*if (
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login'
, \OC_Log::INFO
);
}*/
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
* @param array $params keys: uid, password
*/
@ -119,8 +156,10 @@ class Hooks {
// is in use (client-side encryption does not have access to
// the necessary keys)
if ( Crypt::mode() == 'server' ) {
$session = new Session();
$view = new \OC_FilesystemView( '/' );
$session = new Session($view);
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
@ -165,7 +204,36 @@ 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
*/
@ -187,7 +255,9 @@ class Hooks {
// [fileTarget] => /test8
// [id] => 10
// [token] =>
// [run] => whether emitting script should continue to run
// TODO: Should other kinds of item be encrypted too?
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$view = new \OC_FilesystemView('/');
@ -196,13 +266,55 @@ class Hooks {
$util = new Util($view, $userId);
$path = $util->fileIdToPath($params['itemSource']);
//check if this is a reshare action, that's true if the item source is already shared with me
$sharedItem = \OCP\Share::getItemSharedWithBySource($params['itemType'], $params['itemSource']);
if ($sharedItem) {
// if it is a re-share than the file is located in my Shared folder
$path = '/Shared'.$sharedItem['file_target'];
} else {
$path = $util->fileIdToPath($params['itemSource']);
//if parent is set, then this is a re-share action
if ($params['parent']) {
// get the parent from current share
$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 {
// 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();
@ -216,23 +328,7 @@ class Hooks {
foreach ($allFiles as $path) {
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $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;
$util->setSharedFileKeyfiles( $session, $usersSharing, $path );
}
}
}
@ -241,51 +337,89 @@ class Hooks {
* @brief
*/
public static function postUnshare( $params ) {
// NOTE: $params has keys:
// [itemType] => file
// [itemSource] => 13
// [shareType] => 0
// [shareWith] => test1
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
// [itemParent] =>
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
$view = new \OC_FilesystemView( '/' );
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
$util = new Util( $view, $userId);
$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
if ($params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP) {
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) {
$userIds = \OC_Group::usersInGroup($params['shareWith']);
} else if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_LINK ){
$userIds = array( $util->getPublicShareKeyId() );
} else {
$userIds = array($params['shareWith']);
$userIds = array( $params['shareWith'] );
}
// if we unshare a folder we need a list of all (sub-)files
if ($params['itemType'] === 'folder') {
$allFiles = $util->getAllFiles($path);
if ( $params['itemType'] === 'folder' ) {
$allFiles = $util->getAllFiles( $path );
} else {
$allFiles = array($path);
$allFiles = array( $path );
}
foreach ( $allFiles as $path ) {
// check if the user still has access to the file, otherwise delete share key
$sharingUsers = $util->getSharingUsersArray(true, $path);
$sharingUsers = $util->getSharingUsersArray( true, $path );
// Unshare every user who no longer has access to the file
$delUsers = array_diff($userIds, $sharingUsers);
if ( ! Keymanager::delShareKey( $view, $delUsers, $path ) ) {
$delUsers = array_diff( $userIds, $sharingUsers);
if ( !Keymanager::delShareKey( $view, $delUsers, $path ) ) {
$failed[] = $path;
}
}
}
// If no attempts to set keyfiles failed
if ( empty( $failed ) ) {
@ -296,19 +430,99 @@ class Hooks {
return false;
}
}
}
/**
* @brief
*/
public static function postUnshareAll( $params ) {
// NOTE: It appears that this is never called for files, so
// we may not need to implement it
}
/**
* @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",
"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:",
"None" => "Nichts"
"None" => "Keine"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
"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:",
"None" => "Nichts"
"None" => "Keine"
);

View File

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

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
"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:",
"None" => "Ezer"
"None" => "Bat ere ez"
);

View File

@ -3,5 +3,5 @@
"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:",
"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",
"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:",
"None" => "Nic"
"None" => "Brak"
);

View File

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

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Šifrovanie súborov nastavené.",
"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",
"None" => "Žiadny"
"None" => "Žiadne"
);

View File

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

View File

@ -3,5 +3,5 @@
"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",
"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
*
* @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
*/
public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$key = $view->file_get_contents( $path );
\OC_FileProxy::$enabled = $proxyStatus;
return $key;
}
@ -113,8 +118,8 @@ class Keymanager {
\OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user
$util = new Util($view, \OCP\User::getUser());
list($owner, $filename) = $util->getUidAndFilename($path);
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $path );
$basePath = '/' . $owner . '/files_encryption/keyfiles';
@ -123,22 +128,74 @@ class Keymanager {
if ( !$view->is_dir( $basePath . '/' . $targetPath ) ) {
// create all parent folders
$info=pathinfo($basePath . '/' . $targetPath);
$keyfileFolderName=$view->getLocalFolder($info['dirname']);
if(!file_exists($keyfileFolderName)) {
mkdir($keyfileFolderName, 0750, true);
$info = pathinfo( $basePath . '/' . $targetPath );
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] );
if ( ! file_exists( $keyfileFolderName ) ) {
mkdir( $keyfileFolderName, 0750, true );
}
}
// try reusing key file if part file
if ( self::isPartialFilePath( $targetPath ) ) {
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile );
} else {
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @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
* @param \OC_FilesystemView $view
@ -150,14 +207,27 @@ class Keymanager {
* of the keyfile must be performed by client code
*/
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());
list($owner, $filename) = $util->getUidAndFilename($filePath);
$filePath_f = ltrim( $filename, '/' );
$keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key';
$proxyStatus = \OC_FileProxy::$enabled;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ( $view->file_exists( $keyfilePath ) ) {
@ -226,7 +296,7 @@ class Keymanager {
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption' );
$proxyStatus = \OC_FileProxy::$enabled;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) ) $view->mkdir( '' );
@ -235,7 +305,8 @@ class Keymanager {
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
return $result;
}
/**
@ -261,7 +332,7 @@ class Keymanager {
$view = new \OC_FilesystemView( '/public-keys' );
$proxyStatus = \OC_FileProxy::$enabled;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) ) $view->mkdir( '' );
@ -270,7 +341,7 @@ class Keymanager {
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
return $result;
}
@ -287,23 +358,32 @@ class Keymanager {
*/
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() );
list($owner, $filename) = $util->getUidAndFilename($path);
list( $owner, $filename ) = $util->getUidAndFilename( $path );
$basePath = '/' . $owner . '/files_encryption/share-keys';
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
$proxyStatus = \OC_FileProxy::$enabled;
// try reusing key file if part file
if(self::isPartialFilePath($shareKeyPath)) {
$writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey';
} else {
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
}
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$result = $view->file_put_contents( $writePath, $shareKey );
\OC_FileProxy::$enabled = $proxyStatus;
\OC_FileProxy::$enabled = $proxyStatus;
if (
is_int( $result )
@ -359,7 +439,20 @@ class Keymanager {
*/
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) {
$proxyStatus = \OC_FileProxy::$enabled;
// 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;
\OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user
@ -367,7 +460,7 @@ class Keymanager {
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 ) ) {
$result = $view->file_get_contents( $shareKeyPath );

View File

@ -93,29 +93,29 @@ class Proxy extends \OC_FileProxy {
public function preFile_put_contents( $path, &$data ) {
if ( self::shouldEncrypt( $path ) ) {
if ( self::shouldEncrypt( $path ) ) {
// Stream put contents should have been converted to fopen
// Stream put contents should have been converted to fopen
if ( !is_resource( $data ) ) {
$userId = \OCP\USER::getUser();
$rootView = new \OC_FilesystemView( '/' );
$util = new Util( $rootView, $userId );
$session = new Session( $rootView );
$userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $userId );
$session = new Session( $view );
$privateKey = $session->getPrivateKey();
$filePath = $util->stripUserFilesPath( $path );
// Set the filesize for userland, before encrypting
$size = strlen( $data );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// 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
$shareKey = Keymanager::getShareKey( $rootView, $userId, $filePath );
$shareKey = Keymanager::getShareKey( $view, $userId, $filePath );
// Decrypt the keyfile
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
@ -124,7 +124,7 @@ class Proxy extends \OC_FileProxy {
// Make a new key
$plainKey = Crypt::generateKey();
}
// Encrypt data
@ -134,36 +134,37 @@ class Proxy extends \OC_FileProxy {
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId );
// Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $rootView, $uniqueUserIds );
// Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds );
// Encrypt plain keyfile to multiple sharefiles
// Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys );
// Save sharekeys to user folders
Keymanager::setShareKeys( $rootView, $filePath, $multiEncrypted['keys'] );
Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] );
// Set encrypted keyfile as common varname
$encKey = $multiEncrypted['data'];
// 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
$data = $encData;
// 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
\OC_FileProxy::$enabled = $proxyStatus;
}
}
return true;
return true;
}
/**
* @param string $path Path of file from which has been read
* @param string $data Data that has been read from file
@ -273,136 +274,22 @@ class Proxy extends \OC_FileProxy {
}
/**
* @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 preRename( $oldPath, $newPath )
{
// 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 )
public function postWrite( $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
$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;
$this->handleFile($path);
return true;
}
public function postTouch( $path )
{
$this->handleFile($path);
return true;
}
public function postFopen( $path, &$result ){
@ -518,25 +405,28 @@ class Proxy extends \OC_FileProxy {
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 ) ) {
$cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
$data['size'] = $cached['unencrypted_size'];
}
return $data;
}
// get file info from cache
$cached = \OC\Files\Filesystem::getFileInfo($path, '');
public function postFileSize( $path, $size ) {
// set the real file size
$data['size'] = $cached['unencrypted_size'];
}
$view = new \OC_FilesystemView( '/' );
return $data;
}
public function postFileSize($path, $size)
{
$view = new \OC_FilesystemView('/');
// if path is a folder do nothing
if($view->is_dir($path)) {
if ($view->is_dir($path)) {
return $size;
}
@ -544,14 +434,65 @@ class Proxy extends \OC_FileProxy {
$path_split = explode('/', $path);
$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
$fileInfo = \OC\Files\Filesystem::getFileInfo($path_f);
// if file is encrypted return real file size
if(is_array($fileInfo) && $fileInfo['encrypted'] == 1) {
return $fileInfo['unencrypted_size'];
if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
$size = $fileInfo['unencrypted_size'];
} else {
return $size;
// 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;
}
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,43 +35,64 @@ class Session {
*
* 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;
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 (
! $this->view->file_exists("/public-keys/owncloud.public.key")
|| ! $this->view->file_exists("/owncloud_private_key/owncloud.private.key" )
! $this->view->file_exists( "/public-keys/".$publicShareKeyId.".public.key" )
|| ! $this->view->file_exists( "/owncloud_private_key/".$publicShareKeyId.".private.key" )
) {
$keypair = Crypt::createKeypair();
$keypair = Crypt::createKeypair();
\OC_FileProxy::$enabled = false;
// Save public key
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
$this->view->file_put_contents( '/public-keys/owncloud.public.key', $keypair['publicKey'] );
// Save public key
if (!$view->is_dir('/public-keys')) {
$view->mkdir('/public-keys');
}
$this->view->file_put_contents( '/public-keys/'.$publicShareKeyId.'.public.key', $keypair['publicKey'] );
// Encrypt private key empthy passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
// Save private key
$this->view->file_put_contents( '/owncloud_private_key/'.$publicShareKeyId.'.private.key', $encryptedPrivateKey );
// Encrypt private key empthy passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
// Save private key
$this->view->file_put_contents( '/owncloud_private_key/owncloud.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

@ -72,20 +72,20 @@ class Stream {
private $rootView; // a fsview object set to '/'
public function stream_open( $path, $mode, $options, &$opened_path ) {
$this->userId = \OCP\User::getUser();
if ( ! isset( $this->rootView ) ) {
$this->rootView = new \OC_FilesystemView( '/' );
}
// Strip identifier text from path, this gives us the path relative to data/<user>/files
$this->relPath = str_replace( 'crypt://', '', $path );
$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
$this->relPath = \OC\Files\Filesystem::normalizePath(str_replace( 'crypt://', '', $path ));
// rawPath is relative to the data directory
$this->rawPath = $this->userId . '/files/' . $this->relPath;
$this->rawPath = $util->getUserFilesDir() . $this->relPath;
if (
dirname( $this->rawPath ) == 'streams'
@ -298,7 +298,8 @@ class Stream {
// automatically attempted when the file is written to disk -
// we are handling that separately here and we don't want to
// 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
$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
// is integrated into the next 6126 / 8192 block
@ -437,6 +415,8 @@ class Stream {
$this->size = max( $this->size, $pointer + $length );
$this->unencryptedSize += $length;
\OC_FileProxy::$enabled = $proxyStatus;
return $length;
}
@ -492,14 +472,59 @@ class Stream {
}
public function stream_close() {
$this->flush();
$this->flush();
if (
$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 );

View File

@ -3,8 +3,8 @@
* ownCloud
*
* @author Sam Tuke, Frank Karlitschek
* @copyright 2012 Sam Tuke samtuke@owncloud.com,
* Frank Karlitschek frank@owncloud.org
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>,
* Frank Karlitschek <frank@owncloud.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@ -24,11 +24,8 @@
# Bugs
# ----
# 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
# 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
@ -91,20 +88,13 @@ class Util {
//// TODO: add support for optional recovery in case of lost passphrase / keys
//// 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.
// Sharing:
//// TODO: add support for encrypting to multiple public keys
//// TODO: add support for decrypting to multiple private keys
// Integration testing:
//// TODO: test new encryption with versioning
//// TODO: test new encryption with sharing
//// DONE: test new encryption with sharing
//// TODO: test new encryption with proxies
@ -118,38 +108,65 @@ class Util {
private $shareKeysPath; // Dir containing env keys for shared files
private $publicKeyPath; // Path to user's public key
private $privateKeyPath; // Path to user's private key
private $publicShareKeyId;
private $recoveryKeyId;
private $isPublic;
public function __construct( \OC_FilesystemView $view, $userId, $client = false ) {
$this->view = $view;
$this->userId = $userId;
$this->client = $client;
$this->userDir = '/' . $this->userId;
$this->fileFolderName = 'files';
$this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . '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 = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
$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->fileFolderName = 'files';
$this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . '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 = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
}
}
public function ready() {
if(
!$this->view->file_exists( $this->encryptionDir )
or !$this->view->file_exists( $this->keyfilesPath )
or !$this->view->file_exists( $this->shareKeysPath )
or !$this->view->file_exists( $this->publicKeyPath )
or !$this->view->file_exists( $this->privateKeyPath )
! $this->view->file_exists( $this->encryptionDir )
or ! $this->view->file_exists( $this->keyfilesPath )
or ! $this->view->file_exists( $this->shareKeysPath )
or ! $this->view->file_exists( $this->publicKeyPath )
or ! $this->view->file_exists( $this->privateKeyPath )
) {
return false;
} else {
return true;
}
@ -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;
}
public function getPublicShareKeyId() {
return $this->publicShareKeyId;
}
/**
* @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
* at the start of the uid in db
*/
public function recoveryEnabled() {
public function recoveryEnabledForUser() {
$sql = 'SELECT
recovery
@ -232,16 +264,25 @@ class Util {
$result = $query->execute( $args );
// Set default in case no records found
$recoveryEnabled = 0;
$recoveryEnabled = array();
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,20 +291,33 @@ class Util {
* @param bool $enabled Whether to enable or disable recovery
* @return bool
*/
public function setRecovery( $enabled ) {
public function setRecoveryForUser( $enabled ) {
$sql = 'UPDATE
*PREFIX*encryption
SET
recovery = ?
WHERE
uid = ?';
$recoveryStatus = $this->recoveryEnabledForUser();
// If a record for this user already exists, update it
if ( false === $recoveryStatus ) {
// Ensure value is an integer
$enabled = intval( $enabled );
$sql = 'INSERT INTO `*PREFIX*encryption`
(`uid`,`mode`,`recovery`)
VALUES (?,?,?)';
$args = array( $this->userId, 'server-side', $enabled );
$args = array( $enabled, $this->userId );
// Create a new record instead
} else {
$sql = 'UPDATE
*PREFIX*encryption
SET
recovery = ?
WHERE
uid = ?';
$args = array( $enabled, $this->userId );
}
$query = \OCP\DB::prepare( $sql );
if ( $query->execute( $args ) ) {
@ -282,7 +336,6 @@ class Util {
* @brief Find all files and their encryption status within a directory
* @param string $directory The path of the parent directory to search
* @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.
* /admin/files NOT /backup OR /home/www/oc/data/admin/files
*/
@ -421,26 +474,123 @@ class Util {
return $text;
}
/**
* @brief Check if a given path identifies an encrypted file
* @return true / false
*/
/**
* @brief Check if a given path identifies an encrypted file
* @return true / false
*/
public function isEncryptedPath( $path ) {
// Disable encryption proxy so data retreived is in its
// Disable encryption proxy so data retrieved is in its
// original form
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$data = $this->view->file_get_contents( $path );
\OC_FileProxy::$enabled = true;
// 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);
}
// re-enable proxy
\OC_FileProxy::$enabled = $proxyStatus;
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
* @note e.g. turns '/admin/files/test.txt' into 'test.txt'
*/
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
* @note Expects a path like /uid/files/Shared/filepath
@ -517,7 +682,7 @@ class Util {
stream_copy_to_stream( $plainHandle1, $plainHandle2 );
// Close access to original file
// $this->view->fclose( $plainHandle1 ); // not implemented in view{}
// $this->view->fclose( $plainHandle1 ); // not implemented in view{}
// Delete original plain file so we can rename enc file later
$this->view->unlink( $rawPath );
@ -653,7 +818,7 @@ class Util {
* @return multi-dimensional array. keys: ready, unready
*/
public function filterShareReadyUsers( $unfilteredUsers ) {
// This array will collect the filtered IDs
$readyIds = $unreadyIds = array();
@ -665,8 +830,9 @@ class Util {
// Check that the user is encryption capable, or is the
// public system user 'ownCloud' (for public shares)
if (
$util->ready()
or $user == 'owncloud'
$user == $this->publicShareKeyId
or $user == $this->recoveryKeyId
or $util->ready()
) {
// Construct array of ready UIDs for Keymanager{}
@ -738,25 +904,27 @@ class Util {
* @brief Encrypt keyfile to multiple users
* @param array $users list of users which should be able to access the file
* @param string $filePath path of the file to be shared
* @return bool
*/
public function setSharedFileKeyfiles( Session $session, array $users, $filePath ) {
// Make sure users are capable of sharing
$filteredUids = $this->filterShareReadyUsers( $users );
// trigger_error( print_r($filteredUids, 1) );
// If we're attempting to share to unready users
if ( ! empty( $filteredUids['unready'] ) ) {
// Notify user of unready userDir
// TODO: Move this out of here; it belongs somewhere else
\OCP\JSON::error();
\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 );
return false;
}
// Get public keys for each user, ready for generating sharekeys
$userPubKeys = Keymanager::getPublicKeys( $this->view, $filteredUids['ready'] );
// Note proxy status then disable it
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// 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 new sharekeys to all necessary user directory
// TODO: Reuse the keyfile, it it exists, instead of making a new one
if (
! Keymanager::setFileKey( $this->view, $filePath, $fileOwner, $multiEncKey['data'] )
|| ! 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
// Do this last to ensure file is recoverable in case of error
// Keymanager::deleteFileKey( $this->view, $this->userId, $params['fileTarget'] );
\OC_FileProxy::$enabled = true;
// Return proxy to original status
\OC_FileProxy::$enabled = $proxyStatus;
return true;
}
@ -798,33 +964,51 @@ class Util {
public function getSharingUsersArray( $sharingEnabled, $filePath, $currentUserId = false ) {
// 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
list($owner, $ownerPath) = $this->getUidAndFilename($filePath);
list( $owner, $ownerPath ) = $this->getUidAndFilename( $filePath );
if ( $sharingEnabled ) {
// 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;
}
}
// If recovery is enabled, add the
// Admin UID to list of users to share to
if ( $recoveryEnabled ) {
// FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB?
$adminUid = 'recoveryAdmin';
$userIds[] = $adminUid;
// Find recoveryAdmin user ID
$recoveryKeyId = \OC_Appconfig::getValue( 'files_encryption', 'recoveryKeyId' );
// Add recoveryAdmin to list of users sharing
$userIds[] = $recoveryKeyId;
}
// add current user if given
if($currentUserId != false) {
$userIds[] = $currentUserId;
}
// add current user if given
if ( $currentUserId != false ) {
$userIds[] = $currentUserId;
}
// Remove duplicate UIDs
$uniqueUserIds = array_unique ( $userIds );
@ -832,6 +1016,78 @@ class Util {
return $uniqueUserIds;
}
/**
* @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
@ -842,47 +1098,56 @@ class Util {
*/
public function getUidAndFilename( $path ) {
$fileOwnerUid = \OC\Files\Filesystem::getOwner( $path );
// Check that UID is valid
if ( ! \OCP\User::userExists( $fileOwnerUid ) ) {
throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $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
if ( ! \OCP\User::userExists( $fileOwnerUid ) ) {
throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $path . '"' );
}
// NOTE: Bah, this dependency should be elsewhere
\OC\Files\Filesystem::initMountPoints( $fileOwnerUid );
// If the file owner is the currently logged in user
if ( $fileOwnerUid == $this->userId ) {
// Assume the path supplied is correct
$filename = $path;
} else {
$info = $view->getFileInfo( $path );
$ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' );
// Fetch real file path from DB
$filename = $ownerView->getPath( $info['fileid'] ); // TODO: Check that this returns a path without including the user data dir
}
// Make path relative for use by $view
$relpath = \OC\Files\Filesystem::normalizePath($fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename);
// Check that the filename we're using is working
if ( $this->view->file_exists( $relpath ) ) {
return array ( $fileOwnerUid, $filename );
} else {
return false;
}
}
// NOTE: Bah, this dependency should be elsewhere
\OC\Files\Filesystem::initMountPoints( $fileOwnerUid );
// If the file owner is the currently logged in user
if ( $fileOwnerUid == $this->userId ) {
// Assume the path supplied is correct
$filename = $path;
} else {
$info = \OC\Files\Filesystem::getFileInfo( $path );
$ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' );
// Fetch real file path from DB
$filename = $ownerView->getPath( $info['fileid'] ); // TODO: Check that this returns a path without including the user data dir
}
// Make path relative for use by $view
$relpath = $fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename;
// Check that the filename we're using is working
if ( $this->view->file_exists( $relpath ) ) {
return array ( $fileOwnerUid, $filename );
} else {
return false;
}
}
@ -891,19 +1156,130 @@ class Util {
* @param type $dir 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();
$content = $this->view->getDirectoryContent($this->userFilesDir.$dir);
foreach ($content as $c) {
$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 ) {
$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 {
break;
}
}
$path = $dir.$path;
if ($c['type'] === "dir" ) {
$result = array_merge($result, $this->getAllFiles(substr($c['path'],5)));
$result = array_merge( $result, $this->getAllFiles( $path ) );
} else {
$result[] = substr($c['path'], 5);
$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();
$tmpl = new OCP\Template( 'files_encryption', 'settings' );
$tmpl = new OCP\Template( 'files_encryption', 'settings-admin' );
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
// Check if an adminRecovery account is enabled for recovering files after lost pwd
$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( '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' );
return $tmpl->fetchPage();

View File

@ -6,10 +6,35 @@
* See the COPYING-README file.
*/
// Add CSS stylesheet
\OC_Util::addStyle( 'files_encryption', 'settings-personal' );
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
$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 );
return $tmpl->fetchPage();

View File

@ -4,22 +4,16 @@
<p>
<strong><?php p($l->t( 'Encryption' )); ?></strong>
<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>
<?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 />
<?php if ( empty( $_['recoveryAdminUid'] ) ): ?>
<input type="password" name="recoveryPassword" id="recoveryPassword" />
<label for="recoveryPassword">Recovery account password</label>
<br />
<?php endif; ?>
<input
type='radio'
name='adminEnableRecovery'

View File

@ -1,15 +1,19 @@
<form id="encryption">
<fieldset class="personalblock">
<legend>
<?php p($l->t( 'Encryption' )); ?>
<?php p( $l->t( 'Encryption' ) ); ?>
</legend>
<p>
<?php p($l->t( 'File encryption is enabled.' )); ?>
<!-- <?php p( $l->t( 'File encryption is enabled.' ) ); ?> -->
</p>
<?php if ( ! empty( $_["blacklist"] ) ): ?>
<p>
<?php p($l->t( 'The following file types will not be encrypted:' )); ?>
<strong>File types</strong>
<br />
<?php p( $l->t( 'The following file types will not be encrypted:' ) ); ?>
</p>
<ul>
<?php foreach( $_["blacklist"] as $type ): ?>
<li>
@ -18,5 +22,43 @@
<?php endforeach; ?>
</ul>
<?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>
</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/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;
// This has to go here because otherwise session errors arise, and the private
// 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
@ -34,8 +34,11 @@ use OCA\Encryption;
class Test_Crypt extends \PHPUnit_Framework_TestCase {
function setUp() {
// set content for encrypting / decrypting in tests
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
// set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
$this->dataShort = 'hats';
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
@ -52,17 +55,32 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
\OC_Filesystem::init( '/' );
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
$userHome = \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 testGenerateKey() {
}
function testGenerateKey() {
# TODO: use more accurate (larger) string length for test confirmation
@ -220,40 +238,53 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
//
// }
function testSymmetricStreamEncryptShortFileContent() {
$filename = 'tmp-'.time();
function testSymmetricStreamEncryptShortFileContent() {
$filename = 'tmp-'.time().'.test';
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// 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 );
// 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
$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
$this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
// Get private key
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
// Get keyfile
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Manually decrypt
$manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile );
// Get the encrypted keyfile
$encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
// 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 private key
$privateKey = $session->getPrivateKey( $this->userId );
// Decrypt keyfile with shareKey
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
// Manually decrypt
$manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $retreivedCryptedFile, $plainKeyfile );
// Check that decrypted data matches
$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() {
// Generate a a random filename
$filename = 'tmp-'.time();
$filename = 'tmp-'.time().'.test';
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
@ -273,12 +304,18 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// 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 );
// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n";
// Check that the file was encrypted before being written to disk
// 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
$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
$this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
// Manuallly split saved file into separate IVs and encrypted chunks
@ -290,46 +327,42 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
$e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] );
//print_r($e);
// Get private key
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
// Get keyfile
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Get the encrypted keyfile
$encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
// 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 private key
$privateKey = $session->getPrivateKey( $this->userId );
// Decrypt keyfile with shareKey
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
// Set var for reassembling decrypted content
$decrypt = '';
// Manually decrypt chunk
foreach ($e as $e) {
// echo "\n\$e = $e";
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile );
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $plainKeyfile );
// Assemble decrypted chunks
$decrypt .= $chunkDecrypt;
// echo "\n\$chunkDecrypt = $chunkDecrypt";
}
// echo "\n\$decrypt = $decrypt";
$this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
// Teardown
$this->view->unlink( $filename );
$this->view->unlink( $this->userId . '/files/' . $filename );
Encryption\Keymanager::deleteFileKey( $filename );
Encryption\Keymanager::deleteFileKey( $this->view, $this->userId, $filename );
}
@ -345,15 +378,14 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// 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 );
// Get file decrypted contents
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataShort, $decrypt );
// tear down
$this->view->unlink( $this->userId . '/files/' . $filename );
}
function testSymmetricStreamDecryptLongFileContent() {
@ -365,15 +397,14 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// 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 );
// Get file decrypted contents
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataLong, $decrypt );
// tear down
$this->view->unlink( $this->userId . '/files/' . $filename );
}
// 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(){
//
// $key=uniqid();

View File

@ -13,18 +13,22 @@ 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;
// This has to go here because otherwise session errors arise, and the private
// 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 {
function setUp() {
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
\OC_FileProxy::$enabled = false;
// set content for encrypting / decrypting in tests
@ -38,16 +42,30 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView( '/' );
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
\OC_Filesystem::init( '/' );
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
$this->view = new \OC_FilesystemView( '/' );
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
$userHome = \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(){
@ -59,9 +77,13 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
function testGetPrivateKey() {
$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
$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 );
$this->assertEquals( 451, strlen( $key ) );
$this->assertGreaterThan( 26, strlen( $key ) );
$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' );
$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' );
Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] );
Encryption\Keymanager::setFileKey( $this->view, $file, $this->userId, $key['key'] );
}
@ -109,9 +139,15 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
$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( 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>
// * This file is licensed under the Affero General Public License version 3 or

View File

@ -24,24 +24,23 @@ $loader->register();
use \Mockery as m;
use OCA\Encryption;
\OC_User::login( 'admin', 'admin' );
class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
function setUp() {
\OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
// set content for encrypting / decrypting in tests
// reset backend
\OC_User::useBackend('database');
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
// set content for encrypting / decrypting in tests
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
$this->dataShort = 'hats';
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
$this->userId = 'admin';
$this->pass = 'admin';
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
@ -52,11 +51,29 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
$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->view = new \OC_FilesystemView( '/' );
$this->mockView = m::mock('OC_FilesystemView');
$this->util = new Encryption\Util( $this->mockView, $this->userId );
$this->view = new \OC_FilesystemView( '/' );
$userHome = \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);
$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
*
*
*
*/
function testKeyPaths() {
@ -90,8 +110,8 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false );
$mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true );
$mockView->shouldReceive( 'file_exists' )->times(7)->andReturn( false );
$mockView->shouldReceive( 'mkdir' )->times(6)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$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->shouldReceive( 'file_exists' )->times(6)->andReturn( true );
$mockView->shouldReceive( 'file_exists' )->times(8)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$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->shouldReceive( 'file_exists' )->times(3)->andReturn( true );
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( true );
$util = new Encryption\Util( $mockView, $this->userId );
@ -158,43 +178,57 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
$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
# then false will be returned. Use strict ordering?
}
function testRecoveryEnabled() {
function testRecoveryEnabledForUser() {
$util = new Encryption\Util( $this->view, $this->userId );
// 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
$this->assertTrue( $util->setRecovery( $enabled ) );
$this->assertTrue( $util->setRecoveryForUser( $enabled ) );
}
function testGetUidAndFilename() {
\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} файла",
"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
"Restore" => "Възтановяване",
"Delete" => "Изтриване",
"Deleted Files" => "Изтрити файлове"
"Delete" => "Изтриване"
);

View File

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

View File

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

View File

@ -8,9 +8,9 @@
"Name" => "Nazwa",
"Deleted" => "Usunięte",
"1 folder" => "1 folder",
"{count} folders" => "Ilość folderów: {count}",
"{count} folders" => "{count} foldery",
"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!",
"Restore" => "Przywróć",
"Delete" => "Usuń",

View File

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

View File

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

View File

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

View File

@ -29,6 +29,17 @@ class Trashbin {
// unit: percentage; 50% of available disk space/quota
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
*
@ -62,8 +73,12 @@ class Trashbin {
if ( $trashbinSize === false || $trashbinSize < 0 ) {
$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);
\OC_FileProxy::$enabled = $proxyStatus;
if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) {
$trashbinSize += $sizeOfAddedFiles;
@ -110,13 +125,17 @@ class Trashbin {
\OC_FileProxy::$enabled = false;
$user = \OCP\User::getUser();
if ($view->is_dir('files_versions/' . $file_path)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_versions/' . $file_path));
$view->rename('files_versions/' . $file_path, 'files_trashbin/versions/' . $filename . '.d' . $timestamp);
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path)) {
$rootView = new \OC\Files\View('/');
list($owner, $ownerPath) = self::getUidAndFilename($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) {
$size += $view->filesize('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);
$size += $rootView->filesize($owner.'/files_versions' . $v['path'] . '.v' . $v['version']);
$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')) {
$user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// 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')) {
$user = \OCP\User::getUser();
if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) {
// move keyfiles
if ($view->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$view->rename($keyfile, 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
if ($rootView->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View($keyfile));
$rootView->rename($keyfile, $user.'/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
} else {
$size += $view->filesize($keyfile . '.key');
$view->rename($keyfile . '.key', 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
$size += $rootView->filesize($keyfile . '.key');
$rootView->rename($keyfile . '.key', $user.'/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
}
}
// 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)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
$view->rename($sharekeys, 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
if ($rootView->is_dir($sharekeys)) {
$size += self::calculateSize(new \OC\Files\View($sharekeys));
$rootView->rename($sharekeys, $user.'/files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
} else {
// get local path to share-keys
$localShareKeysPath = $view->getLocalFile($sharekeys);
$localShareKeysPath = $rootView->getLocalFile($sharekeys);
// handle share-keys
$matches = glob(preg_quote($localShareKeysPath).'*.shareKey');
@ -186,10 +208,10 @@ class Trashbin {
if($pathinfo['basename'] == $ownerShareKey) {
// calculate size
$size += $view->filesize($sharekeys. '.' . $user. '.shareKey');
$size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey');
// 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 {
// calculate size
@ -321,6 +343,12 @@ class Trashbin {
\OC_FileProxy::$enabled = false;
$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) {
$versionedFile = $filename;
} else {
@ -329,15 +357,15 @@ class Trashbin {
if ($view->is_dir('/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)) {
foreach ($versions as $v) {
if ($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 {
$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 $filename name of file
* @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
*
* @return size of restored encrypted file
@ -366,20 +394,25 @@ class Trashbin {
$size = 0;
if (\OCP\App::isEnabled('files_encryption')) {
$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);
$source_location = $path_parts['dirname'];
if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) {
if($source_location != '.') {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename);
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename);
} else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $filename);
}
} 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) {
@ -390,35 +423,36 @@ class Trashbin {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($view->file_exists($keyfile)) {
if ($rootView->file_exists($keyfile)) {
// handle directory
if ($view->is_dir($keyfile)) {
if ($rootView->is_dir($keyfile)) {
// handle keyfiles
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext);
$size += self::calculateSize(new \OC\Files\View($keyfile));
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath);
// handle share-keys
if ($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 {
// handle keyfiles
$size += $view->filesize($keyfile);
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key');
$size += $rootView->filesize($keyfile);
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key');
// 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) {
$ownerShareKey .= '.d' . $timestamp;
}
$size += $view->filesize($ownerShareKey);
$size += $rootView->filesize($ownerShareKey);
// 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
$filesystemView = new \OC_FilesystemView('/');
@ -426,7 +460,7 @@ class Trashbin {
$util = new \OCA\Encryption\Util($filesystemView, $user);
// fix the file size
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $user . '/files/'. $location. '/' .$filename);
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath);
$util->fixFileSize($absolutePath);
// get current sharing state
@ -475,7 +509,25 @@ class Trashbin {
$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') ) {
$user = \OCP\User::getUser();
if ($view->is_dir('files_trashbin/versions/'.$file)) {
$size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
$view->unlink('files_trashbin/versions/'.$file);
@ -491,35 +543,37 @@ class Trashbin {
}
}
}
// Take care of encryption keys
$parts = pathinfo($file);
if ( $view->is_dir('/files_trashbin/files/'.$file) ) {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename);
} else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename.'.key');
}
if ($timestamp) {
$keyfile .= '.d'.$timestamp;
}
if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) {
if ( $view->is_dir($keyfile) ) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile));
return $size;
}
private static function deleteEncryptionKeys($view, $file, $filename, $timestamp) {
$size = 0;
if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser();
if ($view->is_dir('/files_trashbin/files/' . $file)) {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
} else {
$size += $view->filesize($keyfile);
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key');
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename . '.' . $user . '.shareKey');
}
if ($timestamp) {
$keyfile .= '.d' . $timestamp;
$sharekeys .= '.d' . $timestamp;
}
if ($view->file_exists($keyfile)) {
if ($view->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
} else {
$size += $view->filesize($keyfile);
$size += $view->filesize($sharekeys);
}
$view->unlink($keyfile);
$view->unlink($sharekeys);
}
$view->unlink($keyfile);
}
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;
}

View File

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