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:
commit
01f1153b08
|
@ -1,6 +1,5 @@
|
||||||
setValue( $app, $key, $value )
|
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
|
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
|
||||||
* This file is licensed under the Affero General Public License version 3 or later.
|
* This file is licensed under the Affero General Public License version 3 or later.
|
||||||
|
@ -8,69 +7,79 @@ setValue( $app, $key, $value )
|
||||||
*
|
*
|
||||||
* @brief Script to handle admin settings for encrypted key recovery
|
* @brief Script to handle admin settings for encrypted key recovery
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use OCA\Encryption;
|
use OCA\Encryption;
|
||||||
|
|
||||||
\OCP\JSON::checkAdminUser();
|
\OCP\JSON::checkAdminUser();
|
||||||
\OCP\JSON::checkAppEnabled('files_encryption');
|
\OCP\JSON::checkAppEnabled('files_encryption');
|
||||||
\OCP\JSON::callCheck();
|
\OCP\JSON::callCheck();
|
||||||
|
|
||||||
$return = $doSetup = false;
|
$return = false;
|
||||||
|
|
||||||
|
// Enable recoveryAdmin
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isset($_POST['adminEnableRecovery'])
|
isset($_POST['adminEnableRecovery'])
|
||||||
&& $_POST['adminEnableRecovery'] == 1
|
&& 1 == $_POST['adminEnableRecovery']
|
||||||
|
) {
|
||||||
|
|
||||||
|
$view = new \OC\Files\View('/');
|
||||||
|
|
||||||
|
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
|
||||||
|
|
||||||
|
if ($recoveryKeyId === null) {
|
||||||
|
$recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8);
|
||||||
|
\OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$view->is_dir('/owncloud_private_key')) {
|
||||||
|
$view->mkdir('/owncloud_private_key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key")
|
||||||
|
|| !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key"))
|
||||||
&& isset($_POST['recoveryPassword'])
|
&& isset($_POST['recoveryPassword'])
|
||||||
&& !empty($_POST['recoveryPassword'])
|
&& !empty($_POST['recoveryPassword'])
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// TODO: Let the admin set this themselves
|
$keypair = \OCA\Encryption\Crypt::createKeypair();
|
||||||
$recoveryAdminUid = 'recoveryAdmin';
|
|
||||||
|
|
||||||
// If desired recoveryAdmin UID is already in use
|
\OC_FileProxy::$enabled = false;
|
||||||
if ( ! \OC_User::userExists( $recoveryAdminUid ) ) {
|
|
||||||
|
|
||||||
// Create new recoveryAdmin user
|
// Save public key
|
||||||
\OC_User::createUser( $recoveryAdminUid, $_POST['recoveryPassword'] );
|
|
||||||
|
|
||||||
$doSetup = true;
|
if (!$view->is_dir('/public-keys')) {
|
||||||
|
$view->mkdir('/public-keys');
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
$view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
|
||||||
|
|
||||||
// Get list of admin users
|
// Encrypt private key empthy passphrase
|
||||||
$admins = OC_Group::usersInGroup( 'admin' );
|
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $_POST['recoveryPassword']);
|
||||||
|
|
||||||
// If the existing recoveryAdmin UID is an admin
|
// Save private key
|
||||||
if ( in_array( $recoveryAdminUid, $admins ) ) {
|
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
|
||||||
|
|
||||||
// The desired recoveryAdmi UID pre-exists and can be used
|
\OC_FileProxy::$enabled = true;
|
||||||
$doSetup = true;
|
|
||||||
|
|
||||||
// If the recoveryAdmin UID exists but doesn't have admin rights
|
|
||||||
} else {
|
|
||||||
|
|
||||||
$return = false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Set recoveryAdmin as enabled
|
||||||
|
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
|
||||||
// If recoveryAdmin has passed other checks
|
|
||||||
if ( $doSetup ) {
|
|
||||||
|
|
||||||
$view = new \OC_FilesystemView( '/' );
|
|
||||||
$util = new Util( $view, $recoveryAdminUid );
|
|
||||||
|
|
||||||
// Ensure recoveryAdmin is ready for encryption (has usable keypair etc.)
|
|
||||||
$util->setupServerSide( $_POST['recoveryPassword'] );
|
|
||||||
|
|
||||||
// Store the UID in the DB
|
|
||||||
OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminUid', $recoveryAdminUid );
|
|
||||||
|
|
||||||
$return = true;
|
$return = true;
|
||||||
|
|
||||||
|
// Disable recoveryAdmin
|
||||||
|
} elseif (
|
||||||
|
isset($_POST['adminEnableRecovery'])
|
||||||
|
&& 0 == $_POST['adminEnableRecovery']
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Set recoveryAdmin as enabled
|
||||||
|
OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0);
|
||||||
|
|
||||||
|
$return = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Return success or failure
|
||||||
|
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();
|
||||||
($return) ? OC_JSON::success() : OC_JSON::error();
|
|
|
@ -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();
|
|
@ -1,5 +1,3 @@
|
||||||
setValue( $app, $key, $value )
|
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
|
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
|
||||||
|
@ -17,26 +15,21 @@ use OCA\Encryption;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isset( $_POST['userEnableRecovery'] )
|
isset( $_POST['userEnableRecovery'] )
|
||||||
|
&& ( 0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'] )
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Ensure preference is an integer
|
|
||||||
$recoveryEnabled = intval( $_POST['userEnableRecovery'] );
|
|
||||||
|
|
||||||
$userId = \OCP\USER::getUser();
|
$userId = \OCP\USER::getUser();
|
||||||
$view = new \OC_FilesystemView( '/' );
|
$view = new \OC_FilesystemView( '/' );
|
||||||
$util = new Util( $view, $userId );
|
$util = new \OCA\Encryption\Util( $view, $userId );
|
||||||
|
|
||||||
// Save recovery preference to DB
|
// Save recovery preference to DB
|
||||||
$result = $util->setRecovery( $recoveryEnabled );
|
$return = $util->setRecoveryForUser( $_POST['userEnableRecovery'] );
|
||||||
|
|
||||||
if ( $result ) {
|
|
||||||
|
|
||||||
\OCP\JSON::success();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
\OCP\JSON::error();
|
$return = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Return success or failure
|
||||||
|
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();
|
|
@ -8,24 +8,25 @@ OC::$CLASSPATH['OCA\Encryption\Stream'] = 'files_encryption/lib/stream.php';
|
||||||
OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php';
|
OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php';
|
||||||
OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
|
OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
|
||||||
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
|
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
|
||||||
|
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
|
||||||
|
|
||||||
OC_FileProxy::register( new OCA\Encryption\Proxy() );
|
OC_FileProxy::register( new OCA\Encryption\Proxy() );
|
||||||
|
|
||||||
// User-related hooks
|
// User related hooks
|
||||||
OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
|
OCA\Encryption\Helper::registerUserHooks();
|
||||||
OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
|
|
||||||
|
|
||||||
// Sharing-related hooks
|
// Sharing related hooks
|
||||||
OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' );
|
OCA\Encryption\Helper::registerShareHooks();
|
||||||
OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' );
|
|
||||||
OCP\Util::connectHook( 'OCP\Share', 'post_unshareAll', 'OCA\Encryption\Hooks', 'postUnshareAll' );
|
|
||||||
|
|
||||||
// Webdav-related hooks
|
// Webdav related hooks
|
||||||
OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfileFromClient' );
|
OCA\Encryption\Helper::registerWebdavHooks();
|
||||||
|
|
||||||
|
// Filesystem related hooks
|
||||||
|
OCA\Encryption\Helper::registerFilesystemHooks();
|
||||||
|
|
||||||
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
|
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
|
||||||
|
|
||||||
$view = new OC_FilesystemView( '/' );
|
$view = new \OC\Files\View( '/' );
|
||||||
|
|
||||||
$session = new OCA\Encryption\Session( $view );
|
$session = new OCA\Encryption\Session( $view );
|
||||||
|
|
||||||
|
@ -47,5 +48,6 @@ if (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register settings scripts
|
// Register settings scripts
|
||||||
OCP\App::registerAdmin( 'files_encryption', 'settings' );
|
OCP\App::registerAdmin( 'files_encryption', 'settings-admin' );
|
||||||
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );
|
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,13 @@
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
<comments>Whether encryption key recovery is enabled</comments>
|
<comments>Whether encryption key recovery is enabled</comments>
|
||||||
</field>
|
</field>
|
||||||
|
<field>
|
||||||
|
<name>migrationStatus</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<notnull>true</notnull>
|
||||||
|
<default>0</default>
|
||||||
|
<comments>Whether encryption migration has been performed</comments>
|
||||||
|
</field>
|
||||||
</declaration>
|
</declaration>
|
||||||
</table>
|
</table>
|
||||||
</database>
|
</database>
|
|
@ -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;
|
||||||
|
}
|
|
@ -23,10 +23,11 @@
|
||||||
|
|
||||||
namespace OCA\Encryption;
|
namespace OCA\Encryption;
|
||||||
|
|
||||||
|
use OC\Files\Filesystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for hook specific logic
|
* Class for hook specific logic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Hooks {
|
class Hooks {
|
||||||
|
|
||||||
// TODO: use passphrase for encrypting private key that is separate to
|
// TODO: use passphrase for encrypting private key that is separate to
|
||||||
|
@ -47,13 +48,9 @@ class Hooks {
|
||||||
|
|
||||||
$util = new Util( $view, $params['uid'] );
|
$util = new Util( $view, $params['uid'] );
|
||||||
|
|
||||||
// Check files_encryption infrastructure is ready for action
|
// setup user, if user not ready force relogin
|
||||||
if ( ! $util->ready() ) {
|
if(Helper::setupUser($util, $params['password']) === false) {
|
||||||
|
return false;
|
||||||
\OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
|
|
||||||
|
|
||||||
return $util->setupServerSide( $params['password'] );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
@ -68,9 +65,13 @@ class Hooks {
|
||||||
|
|
||||||
$session->setPrivateKey( $privateKey, $params['uid'] );
|
$session->setPrivateKey( $privateKey, $params['uid'] );
|
||||||
|
|
||||||
//FIXME: disabled because it gets called each time a user do an operation on iPhone
|
// Check if first-run file migration has already been performed
|
||||||
//FIXME: we need a better place doing this and maybe only one time or by user
|
$migrationCompleted = $util->getMigrationStatus();
|
||||||
/*$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
|
|
||||||
|
// If migration not yet done
|
||||||
|
if ( ! $migrationCompleted ) {
|
||||||
|
|
||||||
|
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
|
||||||
|
|
||||||
// Set legacy encryption key if it exists, to support
|
// Set legacy encryption key if it exists, to support
|
||||||
// depreciated encryption system
|
// depreciated encryption system
|
||||||
|
@ -89,26 +90,62 @@ class Hooks {
|
||||||
|
|
||||||
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
|
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = false;*/
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Encrypt existing user files:
|
// Encrypt existing user files:
|
||||||
// This serves to upgrade old versions of the encryption
|
// This serves to upgrade old versions of the encryption
|
||||||
// app (see appinfo/spec.txt)
|
// app (see appinfo/spec.txt)
|
||||||
/*if (
|
if (
|
||||||
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
|
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
|
||||||
) {
|
) {
|
||||||
|
|
||||||
\OC_Log::write(
|
\OC_Log::write(
|
||||||
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login'
|
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
|
||||||
, \OC_Log::INFO
|
, \OC_Log::INFO
|
||||||
);
|
);
|
||||||
|
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
// Register successful migration in DB
|
||||||
|
$util->setMigrationStatus( 1 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setup encryption backend upon user created
|
||||||
|
* @note This method should never be called for users using client side encryption
|
||||||
|
*/
|
||||||
|
public static function postCreateUser( $params ) {
|
||||||
|
$view = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
|
$util = new Util( $view, $params['uid'] );
|
||||||
|
|
||||||
|
Helper::setupUser($util, $params['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief cleanup encryption backend upon user deleted
|
||||||
|
* @note This method should never be called for users using client side encryption
|
||||||
|
*/
|
||||||
|
public static function postDeleteUser( $params ) {
|
||||||
|
$view = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
|
// cleanup public key
|
||||||
|
$publicKey = '/public-keys/' . $params['uid'] . '.public.key';
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$view->unlink($publicKey);
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Change a user's encryption passphrase
|
* @brief Change a user's encryption passphrase
|
||||||
* @param array $params keys: uid, password
|
* @param array $params keys: uid, password
|
||||||
|
@ -120,7 +157,9 @@ class Hooks {
|
||||||
// the necessary keys)
|
// the necessary keys)
|
||||||
if ( Crypt::mode() == 'server' ) {
|
if ( Crypt::mode() == 'server' ) {
|
||||||
|
|
||||||
$session = new Session();
|
$view = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
|
$session = new Session($view);
|
||||||
|
|
||||||
// Get existing decrypted private key
|
// Get existing decrypted private key
|
||||||
$privateKey = $session->getPrivateKey();
|
$privateKey = $session->getPrivateKey();
|
||||||
|
@ -166,6 +205,35 @@ class Hooks {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief check if files can be encrypted to every user.
|
||||||
|
*/
|
||||||
|
public static function preShared($params) {
|
||||||
|
|
||||||
|
$users = array();
|
||||||
|
$view = new \OC\Files\View('/public-keys/');
|
||||||
|
|
||||||
|
switch ($params['shareType']) {
|
||||||
|
case \OCP\Share::SHARE_TYPE_USER:
|
||||||
|
$users[] = $params['shareWith'];
|
||||||
|
break;
|
||||||
|
case \OCP\Share::SHARE_TYPE_GROUP:
|
||||||
|
$users = \OC_Group::usersInGroup($params['shareWith']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
if (!$view->file_exists($user . '.public.key')) {
|
||||||
|
// Set flag var 'run' to notify emitting
|
||||||
|
// script that hook execution failed
|
||||||
|
$params['run']->run = false;
|
||||||
|
// TODO: Make sure files_sharing provides user
|
||||||
|
// feedback on failed share
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
*/
|
*/
|
||||||
|
@ -187,7 +255,9 @@ class Hooks {
|
||||||
// [fileTarget] => /test8
|
// [fileTarget] => /test8
|
||||||
// [id] => 10
|
// [id] => 10
|
||||||
// [token] =>
|
// [token] =>
|
||||||
|
// [run] => whether emitting script should continue to run
|
||||||
// TODO: Should other kinds of item be encrypted too?
|
// TODO: Should other kinds of item be encrypted too?
|
||||||
|
|
||||||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
|
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
|
||||||
|
|
||||||
$view = new \OC_FilesystemView('/');
|
$view = new \OC_FilesystemView('/');
|
||||||
|
@ -196,13 +266,55 @@ class Hooks {
|
||||||
$util = new Util($view, $userId);
|
$util = new Util($view, $userId);
|
||||||
$path = $util->fileIdToPath($params['itemSource']);
|
$path = $util->fileIdToPath($params['itemSource']);
|
||||||
|
|
||||||
//check if this is a reshare action, that's true if the item source is already shared with me
|
//if parent is set, then this is a re-share action
|
||||||
$sharedItem = \OCP\Share::getItemSharedWithBySource($params['itemType'], $params['itemSource']);
|
if ($params['parent']) {
|
||||||
if ($sharedItem) {
|
|
||||||
// if it is a re-share than the file is located in my Shared folder
|
// get the parent from current share
|
||||||
$path = '/Shared'.$sharedItem['file_target'];
|
$parent = $util->getShareParent($params['parent']);
|
||||||
|
|
||||||
|
// if parent is file the it is an 1:1 share
|
||||||
|
if ($parent['item_type'] === 'file') {
|
||||||
|
|
||||||
|
// prefix path with Shared
|
||||||
|
$path = '/Shared' . $parent['file_target'];
|
||||||
} else {
|
} else {
|
||||||
$path = $util->fileIdToPath($params['itemSource']);
|
|
||||||
|
// NOTE: parent is folder but shared was a file!
|
||||||
|
// we try to rebuild the missing path
|
||||||
|
// some examples we face here
|
||||||
|
// user1 share folder1 with user2 folder1 has
|
||||||
|
// the following structure
|
||||||
|
// /folder1/subfolder1/subsubfolder1/somefile.txt
|
||||||
|
// user2 re-share subfolder2 with user3
|
||||||
|
// user3 re-share somefile.txt user4
|
||||||
|
// so our path should be
|
||||||
|
// /Shared/subfolder1/subsubfolder1/somefile.txt
|
||||||
|
// while user3 is sharing
|
||||||
|
|
||||||
|
if ($params['itemType'] === 'file') {
|
||||||
|
// get target path
|
||||||
|
$targetPath = $util->fileIdToPath($params['fileSource']);
|
||||||
|
$targetPathSplit = array_reverse(explode('/', $targetPath));
|
||||||
|
|
||||||
|
// init values
|
||||||
|
$path = '';
|
||||||
|
$sharedPart = ltrim($parent['file_target'], '/');
|
||||||
|
|
||||||
|
// rebuild path
|
||||||
|
foreach ($targetPathSplit as $pathPart) {
|
||||||
|
if ($pathPart !== $sharedPart) {
|
||||||
|
$path = '/' . $pathPart . $path;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// prefix path with Shared
|
||||||
|
$path = '/Shared' . $parent['file_target'] . $path;
|
||||||
|
} else {
|
||||||
|
// prefix path with Shared
|
||||||
|
$path = '/Shared' . $parent['file_target'] . $params['fileTarget'];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sharingEnabled = \OCP\Share::isEnabled();
|
$sharingEnabled = \OCP\Share::isEnabled();
|
||||||
|
@ -216,23 +328,7 @@ class Hooks {
|
||||||
|
|
||||||
foreach ($allFiles as $path) {
|
foreach ($allFiles as $path) {
|
||||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
||||||
|
$util->setSharedFileKeyfiles( $session, $usersSharing, $path );
|
||||||
$failed = array();
|
|
||||||
|
|
||||||
// Attempt to set shareKey
|
|
||||||
if (!$util->setSharedFileKeyfiles($session, $usersSharing, $path)) {
|
|
||||||
|
|
||||||
$failed[] = $path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no attempts to set keyfiles failed
|
|
||||||
if (empty($failed)) {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,30 +343,67 @@ class Hooks {
|
||||||
// [itemSource] => 13
|
// [itemSource] => 13
|
||||||
// [shareType] => 0
|
// [shareType] => 0
|
||||||
// [shareWith] => test1
|
// [shareWith] => test1
|
||||||
|
// [itemParent] =>
|
||||||
|
|
||||||
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
|
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
|
||||||
|
|
||||||
$view = new \OC_FilesystemView( '/' );
|
$view = new \OC_FilesystemView( '/' );
|
||||||
$session = new Session($view);
|
|
||||||
$userId = \OCP\User::getUser();
|
$userId = \OCP\User::getUser();
|
||||||
$util = new Util( $view, $userId);
|
$util = new Util( $view, $userId);
|
||||||
$path = $util->fileIdToPath( $params['itemSource'] );
|
$path = $util->fileIdToPath( $params['itemSource'] );
|
||||||
|
|
||||||
|
// check if this is a re-share
|
||||||
|
if ( $params['itemParent'] ) {
|
||||||
|
|
||||||
|
// get the parent from current share
|
||||||
|
$parent = $util->getShareParent( $params['itemParent'] );
|
||||||
|
|
||||||
|
// get target path
|
||||||
|
$targetPath = $util->fileIdToPath( $params['itemSource'] );
|
||||||
|
$targetPathSplit = array_reverse( explode( '/', $targetPath ) );
|
||||||
|
|
||||||
|
// init values
|
||||||
|
$path = '';
|
||||||
|
$sharedPart = ltrim( $parent['file_target'], '/' );
|
||||||
|
|
||||||
|
// rebuild path
|
||||||
|
foreach ( $targetPathSplit as $pathPart ) {
|
||||||
|
|
||||||
|
if ( $pathPart !== $sharedPart ) {
|
||||||
|
|
||||||
|
$path = '/' . $pathPart . $path;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix path with Shared
|
||||||
|
$path = '/Shared' . $parent['file_target'] . $path;
|
||||||
|
}
|
||||||
|
|
||||||
// for group shares get a list of the group members
|
// for group shares get a list of the group members
|
||||||
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) {
|
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) {
|
||||||
$userIds = \OC_Group::usersInGroup($params['shareWith']);
|
$userIds = \OC_Group::usersInGroup($params['shareWith']);
|
||||||
|
} else if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_LINK ){
|
||||||
|
$userIds = array( $util->getPublicShareKeyId() );
|
||||||
} else {
|
} else {
|
||||||
$userIds = array( $params['shareWith'] );
|
$userIds = array( $params['shareWith'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we unshare a folder we need a list of all (sub-)files
|
// if we unshare a folder we need a list of all (sub-)files
|
||||||
if ( $params['itemType'] === 'folder' ) {
|
if ( $params['itemType'] === 'folder' ) {
|
||||||
|
|
||||||
$allFiles = $util->getAllFiles( $path );
|
$allFiles = $util->getAllFiles( $path );
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$allFiles = array( $path );
|
$allFiles = array( $path );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach ( $allFiles as $path ) {
|
foreach ( $allFiles as $path ) {
|
||||||
|
|
||||||
// check if the user still has access to the file, otherwise delete share key
|
// check if the user still has access to the file, otherwise delete share key
|
||||||
|
@ -278,6 +411,7 @@ class Hooks {
|
||||||
|
|
||||||
// Unshare every user who no longer has access to the file
|
// Unshare every user who no longer has access to the file
|
||||||
$delUsers = array_diff( $userIds, $sharingUsers);
|
$delUsers = array_diff( $userIds, $sharingUsers);
|
||||||
|
|
||||||
if ( !Keymanager::delShareKey( $view, $delUsers, $path ) ) {
|
if ( !Keymanager::delShareKey( $view, $delUsers, $path ) ) {
|
||||||
|
|
||||||
$failed[] = $path;
|
$failed[] = $path;
|
||||||
|
@ -296,9 +430,7 @@ class Hooks {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -311,4 +443,86 @@ class Hooks {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
|
||||||
|
* @param array with oldpath and newpath
|
||||||
|
*
|
||||||
|
* This function is connected to the rename signal of OC_Filesystem and adjust the name and location
|
||||||
|
* of the stored versions along the actual file
|
||||||
|
*/
|
||||||
|
public static function postRename($params) {
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$view = new \OC_FilesystemView('/');
|
||||||
|
$session = new Session($view);
|
||||||
|
$userId = \OCP\User::getUser();
|
||||||
|
$util = new Util( $view, $userId );
|
||||||
|
|
||||||
|
// Format paths to be relative to user files dir
|
||||||
|
$oldKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']);
|
||||||
|
$newKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']);
|
||||||
|
|
||||||
|
// add key ext if this is not an folder
|
||||||
|
if (!$view->is_dir($oldKeyfilePath)) {
|
||||||
|
$oldKeyfilePath .= '.key';
|
||||||
|
$newKeyfilePath .= '.key';
|
||||||
|
|
||||||
|
// handle share-keys
|
||||||
|
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$params['oldpath']);
|
||||||
|
$matches = glob(preg_quote($localKeyPath).'*.shareKey');
|
||||||
|
foreach ($matches as $src) {
|
||||||
|
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
|
||||||
|
|
||||||
|
// create destination folder if not exists
|
||||||
|
if(!file_exists(dirname($dst))) {
|
||||||
|
mkdir(dirname($dst), 0750, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
rename($src, $dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// handle share-keys folders
|
||||||
|
$oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']);
|
||||||
|
$newShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']);
|
||||||
|
|
||||||
|
// create destination folder if not exists
|
||||||
|
if(!$view->file_exists(dirname($newShareKeyfilePath))) {
|
||||||
|
$view->mkdir(dirname($newShareKeyfilePath), 0750, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename keyfile so it isn't orphaned
|
||||||
|
if($view->file_exists($oldKeyfilePath)) {
|
||||||
|
|
||||||
|
// create destination folder if not exists
|
||||||
|
if(!$view->file_exists(dirname($newKeyfilePath))) {
|
||||||
|
$view->mkdir(dirname($newKeyfilePath), 0750, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->rename($oldKeyfilePath, $newKeyfilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the path to the file
|
||||||
|
$newPath = '/' . $userId . '/files' .$params['newpath'];
|
||||||
|
$newPathRelative = $params['newpath'];
|
||||||
|
|
||||||
|
if($util->fixFileSize($newPath)) {
|
||||||
|
// get sharing app state
|
||||||
|
$sharingEnabled = \OCP\Share::isEnabled();
|
||||||
|
|
||||||
|
// get users
|
||||||
|
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
|
||||||
|
|
||||||
|
// update sharing-keys
|
||||||
|
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
|
||||||
|
}
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
})
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
})
|
|
@ -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);
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert",
|
"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert",
|
||||||
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
|
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
|
||||||
"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:",
|
"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:",
|
||||||
"None" => "Nichts"
|
"None" => "Keine"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
|
"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
|
||||||
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
|
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
|
||||||
"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:",
|
"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:",
|
||||||
"None" => "Nichts"
|
"None" => "Keine"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.",
|
"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.",
|
||||||
"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:",
|
"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:",
|
||||||
"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:",
|
"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:",
|
||||||
"None" => "Τίποτα"
|
"None" => "Καμία"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
|
"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
|
||||||
"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:",
|
"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:",
|
||||||
"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:",
|
"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:",
|
||||||
"None" => "Ezer"
|
"None" => "Bat ere ez"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "La cifratura dei file è abilitata.",
|
"File encryption is enabled." => "La cifratura dei file è abilitata.",
|
||||||
"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:",
|
"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:",
|
||||||
"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:",
|
"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:",
|
||||||
"None" => "Nessuno"
|
"None" => "Nessuna"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Szyfrowanie plików jest włączone",
|
"File encryption is enabled." => "Szyfrowanie plików jest włączone",
|
||||||
"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:",
|
"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:",
|
||||||
"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:",
|
"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:",
|
||||||
"None" => "Nic"
|
"None" => "Brak"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "A criptografia de arquivos está ativada.",
|
"File encryption is enabled." => "A criptografia de arquivos está ativada.",
|
||||||
"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:",
|
"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:",
|
||||||
"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:",
|
"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:",
|
||||||
"None" => "Nada"
|
"None" => "Nenhuma"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Шифрование файла включено.",
|
"File encryption is enabled." => "Шифрование файла включено.",
|
||||||
"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:",
|
"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:",
|
||||||
"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:",
|
"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:",
|
||||||
"None" => "Нет новостей"
|
"None" => "Ничего"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Šifrovanie súborov nastavené.",
|
"File encryption is enabled." => "Šifrovanie súborov nastavené.",
|
||||||
"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:",
|
"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:",
|
||||||
"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov",
|
"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov",
|
||||||
"None" => "Žiadny"
|
"None" => "Žiadne"
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?php $TRANSLATIONS = array(
|
<?php $TRANSLATIONS = array(
|
||||||
"Encryption" => "การเข้ารหัส",
|
"Encryption" => "การเข้ารหัส",
|
||||||
"None" => "ไม่มี"
|
"None" => "ไม่ต้อง"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"File encryption is enabled." => "Mã hóa file đã mở",
|
"File encryption is enabled." => "Mã hóa file đã mở",
|
||||||
"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa",
|
"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa",
|
||||||
"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau",
|
"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau",
|
||||||
"None" => "Không gì cả"
|
"None" => "Không có gì hết"
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,15 +32,20 @@ class Keymanager {
|
||||||
/**
|
/**
|
||||||
* @brief retrieve the ENCRYPTED private key from a user
|
* @brief retrieve the ENCRYPTED private key from a user
|
||||||
*
|
*
|
||||||
* @return string private key or false
|
* @return string private key or false (hopefully)
|
||||||
* @note the key returned by this method must be decrypted before use
|
* @note the key returned by this method must be decrypted before use
|
||||||
*/
|
*/
|
||||||
public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
|
public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
|
||||||
|
|
||||||
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
|
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
|
||||||
|
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
$key = $view->file_get_contents( $path );
|
$key = $view->file_get_contents( $path );
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,13 +130,24 @@ class Keymanager {
|
||||||
// create all parent folders
|
// create all parent folders
|
||||||
$info = pathinfo( $basePath . '/' . $targetPath );
|
$info = pathinfo( $basePath . '/' . $targetPath );
|
||||||
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] );
|
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] );
|
||||||
|
|
||||||
if ( ! file_exists( $keyfileFolderName ) ) {
|
if ( ! file_exists( $keyfileFolderName ) ) {
|
||||||
|
|
||||||
mkdir( $keyfileFolderName, 0750, true );
|
mkdir( $keyfileFolderName, 0750, true );
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try reusing key file if part file
|
||||||
|
if ( self::isPartialFilePath( $targetPath ) ) {
|
||||||
|
|
||||||
|
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
|
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = $proxyStatus;
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
@ -139,6 +155,47 @@ class Keymanager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove .path extension from a file path
|
||||||
|
* @param string $path Path that may identify a .part file
|
||||||
|
* @return string File path without .part extension
|
||||||
|
* @note this is needed for reusing keys
|
||||||
|
*/
|
||||||
|
public static function fixPartialFilePath( $path ) {
|
||||||
|
|
||||||
|
if (preg_match('/\.part$/', $path)) {
|
||||||
|
|
||||||
|
$newLength = strlen($path) - 5;
|
||||||
|
$fPath = substr($path, 0, $newLength);
|
||||||
|
|
||||||
|
return $fPath;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a path is a .part file
|
||||||
|
* @param string $path Path that may identify a .part file
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isPartialFilePath( $path ) {
|
||||||
|
|
||||||
|
if ( preg_match('/\.part$/', $path ) ) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief retrieve keyfile for an encrypted file
|
* @brief retrieve keyfile for an encrypted file
|
||||||
* @param \OC_FilesystemView $view
|
* @param \OC_FilesystemView $view
|
||||||
|
@ -151,6 +208,19 @@ class Keymanager {
|
||||||
*/
|
*/
|
||||||
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) {
|
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) {
|
||||||
|
|
||||||
|
// try reusing key file if part file
|
||||||
|
if ( self::isPartialFilePath( $filePath ) ) {
|
||||||
|
|
||||||
|
$result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) );
|
||||||
|
|
||||||
|
if ( $result ) {
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$util = new Util($view, \OCP\User::getUser());
|
$util = new Util($view, \OCP\User::getUser());
|
||||||
list($owner, $filename) = $util->getUidAndFilename($filePath);
|
list($owner, $filename) = $util->getUidAndFilename($filePath);
|
||||||
$filePath_f = ltrim( $filename, '/' );
|
$filePath_f = ltrim( $filename, '/' );
|
||||||
|
@ -236,6 +306,7 @@ class Keymanager {
|
||||||
\OC_FileProxy::$enabled = $proxyStatus;
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,7 +358,7 @@ class Keymanager {
|
||||||
*/
|
*/
|
||||||
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) {
|
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) {
|
||||||
|
|
||||||
//here we need the currently logged in user, while userId can be a different user
|
// Here we need the currently logged in user, while userId can be a different user
|
||||||
$util = new Util( $view, \OCP\User::getUser() );
|
$util = new Util( $view, \OCP\User::getUser() );
|
||||||
|
|
||||||
list( $owner, $filename ) = $util->getUidAndFilename( $path );
|
list( $owner, $filename ) = $util->getUidAndFilename( $path );
|
||||||
|
@ -296,8 +367,17 @@ class Keymanager {
|
||||||
|
|
||||||
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
|
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
|
||||||
|
|
||||||
|
// try reusing key file if part file
|
||||||
|
if(self::isPartialFilePath($shareKeyPath)) {
|
||||||
|
|
||||||
|
$writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
|
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
@ -359,6 +439,19 @@ class Keymanager {
|
||||||
*/
|
*/
|
||||||
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) {
|
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) {
|
||||||
|
|
||||||
|
// try reusing key file if part file
|
||||||
|
if(self::isPartialFilePath($filePath)) {
|
||||||
|
|
||||||
|
$result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath));
|
||||||
|
|
||||||
|
if($result) {
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
@ -367,7 +460,7 @@ class Keymanager {
|
||||||
|
|
||||||
list($owner, $filename) = $util->getUidAndFilename($filePath);
|
list($owner, $filename) = $util->getUidAndFilename($filePath);
|
||||||
|
|
||||||
$shareKeyPath = '/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey';
|
$shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey');
|
||||||
if ( $view->file_exists( $shareKeyPath ) ) {
|
if ( $view->file_exists( $shareKeyPath ) ) {
|
||||||
|
|
||||||
$result = $view->file_get_contents( $shareKeyPath );
|
$result = $view->file_get_contents( $shareKeyPath );
|
||||||
|
|
|
@ -99,9 +99,9 @@ class Proxy extends \OC_FileProxy {
|
||||||
if ( !is_resource( $data ) ) {
|
if ( !is_resource( $data ) ) {
|
||||||
|
|
||||||
$userId = \OCP\USER::getUser();
|
$userId = \OCP\USER::getUser();
|
||||||
$rootView = new \OC_FilesystemView( '/' );
|
$view = new \OC_FilesystemView( '/' );
|
||||||
$util = new Util( $rootView, $userId );
|
$util = new Util( $view, $userId );
|
||||||
$session = new Session( $rootView );
|
$session = new Session( $view );
|
||||||
$privateKey = $session->getPrivateKey();
|
$privateKey = $session->getPrivateKey();
|
||||||
$filePath = $util->stripUserFilesPath( $path );
|
$filePath = $util->stripUserFilesPath( $path );
|
||||||
// Set the filesize for userland, before encrypting
|
// Set the filesize for userland, before encrypting
|
||||||
|
@ -112,10 +112,10 @@ class Proxy extends \OC_FileProxy {
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Check if there is an existing key we can reuse
|
// Check if there is an existing key we can reuse
|
||||||
if ( $encKeyfile = Keymanager::getFileKey( $rootView, $userId, $filePath ) ) {
|
if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) {
|
||||||
|
|
||||||
// Fetch shareKey
|
// Fetch shareKey
|
||||||
$shareKey = Keymanager::getShareKey( $rootView, $userId, $filePath );
|
$shareKey = Keymanager::getShareKey( $view, $userId, $filePath );
|
||||||
|
|
||||||
// Decrypt the keyfile
|
// Decrypt the keyfile
|
||||||
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
|
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
|
||||||
|
@ -135,25 +135,25 @@ class Proxy extends \OC_FileProxy {
|
||||||
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId );
|
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId );
|
||||||
|
|
||||||
// Fetch public keys for all users who will share the file
|
// Fetch public keys for all users who will share the file
|
||||||
$publicKeys = Keymanager::getPublicKeys( $rootView, $uniqueUserIds );
|
$publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds );
|
||||||
|
|
||||||
// Encrypt plain keyfile to multiple sharefiles
|
// Encrypt plain keyfile to multiple sharefiles
|
||||||
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys );
|
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys );
|
||||||
|
|
||||||
// Save sharekeys to user folders
|
// Save sharekeys to user folders
|
||||||
Keymanager::setShareKeys( $rootView, $filePath, $multiEncrypted['keys'] );
|
Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] );
|
||||||
|
|
||||||
// Set encrypted keyfile as common varname
|
// Set encrypted keyfile as common varname
|
||||||
$encKey = $multiEncrypted['data'];
|
$encKey = $multiEncrypted['data'];
|
||||||
|
|
||||||
// Save keyfile for newly encrypted file in parallel directory tree
|
// Save keyfile for newly encrypted file in parallel directory tree
|
||||||
Keymanager::setFileKey( $rootView, $filePath, $userId, $encKey );
|
Keymanager::setFileKey( $view, $filePath, $userId, $encKey );
|
||||||
|
|
||||||
// Replace plain content with encrypted content by reference
|
// Replace plain content with encrypted content by reference
|
||||||
$data = $encData;
|
$data = $encData;
|
||||||
|
|
||||||
// Update the file cache with file info
|
// Update the file cache with file info
|
||||||
\OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' );
|
\OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted'=>true, 'size' => strlen($size), 'unencrypted_size' => $size), '' );
|
||||||
|
|
||||||
// Re-enable proxy - our work is done
|
// Re-enable proxy - our work is done
|
||||||
\OC_FileProxy::$enabled = $proxyStatus;
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
@ -162,6 +162,7 @@ class Proxy extends \OC_FileProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,132 +278,18 @@ class Proxy extends \OC_FileProxy {
|
||||||
* @return bool Result of rename()
|
* @return bool Result of rename()
|
||||||
* @note This is pre rather than post because using post didn't work
|
* @note This is pre rather than post because using post didn't work
|
||||||
*/
|
*/
|
||||||
public function preRename( $oldPath, $newPath )
|
public function postWrite( $path )
|
||||||
{
|
{
|
||||||
|
$this->handleFile($path);
|
||||||
// Disable encryption proxy to prevent recursive calls
|
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
|
||||||
\OC_FileProxy::$enabled = false;
|
|
||||||
|
|
||||||
$view = new \OC_FilesystemView('/');
|
|
||||||
|
|
||||||
$userId = \OCP\USER::getUser();
|
|
||||||
|
|
||||||
// Format paths to be relative to user files dir
|
|
||||||
$oldTrimmed = ltrim($oldPath, '/');
|
|
||||||
$oldSplit = explode('/', $oldTrimmed);
|
|
||||||
$oldSliced = array_slice($oldSplit, 2);
|
|
||||||
$oldRelPath = implode('/', $oldSliced);
|
|
||||||
$oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath;
|
|
||||||
|
|
||||||
$newTrimmed = ltrim($newPath, '/');
|
|
||||||
$newSplit = explode('/', $newTrimmed);
|
|
||||||
$newSliced = array_slice($newSplit, 2);
|
|
||||||
$newRelPath = implode('/', $newSliced);
|
|
||||||
$newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath;
|
|
||||||
|
|
||||||
// add key ext if this is not an folder
|
|
||||||
if (!$view->is_dir($oldKeyfilePath)) {
|
|
||||||
$oldKeyfilePath .= '.key';
|
|
||||||
$newKeyfilePath .= '.key';
|
|
||||||
|
|
||||||
// handle share-keys
|
|
||||||
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$oldRelPath);
|
|
||||||
$matches = glob(preg_quote($localKeyPath).'*.shareKey');
|
|
||||||
foreach ($matches as $src) {
|
|
||||||
$dst = str_replace($oldRelPath, $newRelPath, $src);
|
|
||||||
rename($src, $dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// handle share-keys folders
|
|
||||||
$oldShareKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $oldRelPath;
|
|
||||||
$newShareKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $newRelPath;
|
|
||||||
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename keyfile so it isn't orphaned
|
|
||||||
$result = $view->rename($oldKeyfilePath, $newKeyfilePath);
|
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = $proxyStatus;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief When a file is renamed, rename its keyfile also
|
|
||||||
* @return bool Result of rename()
|
|
||||||
* @note This is pre rather than post because using post didn't work
|
|
||||||
*/
|
|
||||||
public function postRename( $oldPath, $newPath )
|
|
||||||
{
|
|
||||||
|
|
||||||
// Disable encryption proxy to prevent recursive calls
|
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
|
||||||
\OC_FileProxy::$enabled = false;
|
|
||||||
|
|
||||||
$view = new \OC_FilesystemView('/');
|
|
||||||
$session = new Session($view);
|
|
||||||
$userId = \OCP\User::getUser();
|
|
||||||
$util = new Util( $view, $userId );
|
|
||||||
|
|
||||||
// Reformat path for use with OC_FSV
|
|
||||||
$newPathSplit = explode( '/', $newPath );
|
|
||||||
$newPathRelative = implode( '/', array_slice( $newPathSplit, 3 ) );
|
|
||||||
|
|
||||||
// get file info from database/cache
|
|
||||||
//$newFileInfo = \OC\Files\Filesystem::getFileInfo($newPathRelative);
|
|
||||||
|
|
||||||
if ($util->isEncryptedPath($newPath)) {
|
|
||||||
$cached = $view->getFileInfo($newPath);
|
|
||||||
$cached['encrypted'] = 1;
|
|
||||||
|
|
||||||
// get the size from filesystem
|
|
||||||
$size = $view->filesize($newPath);
|
|
||||||
|
|
||||||
// calculate last chunk nr
|
|
||||||
$lastChunckNr = floor($size / 8192);
|
|
||||||
|
|
||||||
// open stream
|
|
||||||
$result = fopen('crypt://' . $newPathRelative, "r");
|
|
||||||
|
|
||||||
if(is_resource($result)) {
|
|
||||||
// calculate last chunk position
|
|
||||||
$lastChunckPos = ($lastChunckNr * 8192);
|
|
||||||
|
|
||||||
// seek to end
|
|
||||||
fseek($result, $lastChunckPos);
|
|
||||||
|
|
||||||
// get the content of the last chunck
|
|
||||||
$lastChunkContent = fread($result, 8192);
|
|
||||||
|
|
||||||
// calc the real file size with the size of the last chunk
|
|
||||||
$realSize = (($lastChunckNr * 6126) + strlen($lastChunkContent));
|
|
||||||
|
|
||||||
// set the size
|
|
||||||
$cached['unencrypted_size'] = $realSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
$view->putFileInfo( $newPath, $cached );
|
|
||||||
|
|
||||||
// get sharing app state
|
|
||||||
$sharingEnabled = \OCP\Share::isEnabled();
|
|
||||||
|
|
||||||
// get users
|
|
||||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
|
|
||||||
|
|
||||||
// update sharing-keys
|
|
||||||
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = $proxyStatus;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postTouch( $path )
|
||||||
|
{
|
||||||
|
$this->handleFile($path);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postFopen( $path, &$result ){
|
public function postFopen( $path, &$result ){
|
||||||
|
@ -518,20 +405,23 @@ class Proxy extends \OC_FileProxy {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postStat( $path, $data ) {
|
public function postStat($path, $data)
|
||||||
|
{
|
||||||
|
// check if file is encrypted
|
||||||
if (Crypt::isCatfileContent($path)) {
|
if (Crypt::isCatfileContent($path)) {
|
||||||
|
|
||||||
|
// get file info from cache
|
||||||
$cached = \OC\Files\Filesystem::getFileInfo($path, '');
|
$cached = \OC\Files\Filesystem::getFileInfo($path, '');
|
||||||
|
|
||||||
|
// set the real file size
|
||||||
$data['size'] = $cached['unencrypted_size'];
|
$data['size'] = $cached['unencrypted_size'];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postFileSize( $path, $size ) {
|
public function postFileSize($path, $size)
|
||||||
|
{
|
||||||
|
|
||||||
$view = new \OC_FilesystemView('/');
|
$view = new \OC_FilesystemView('/');
|
||||||
|
|
||||||
|
@ -544,14 +434,65 @@ class Proxy extends \OC_FileProxy {
|
||||||
$path_split = explode('/', $path);
|
$path_split = explode('/', $path);
|
||||||
$path_f = implode('/', array_slice($path_split, 3));
|
$path_f = implode('/', array_slice($path_split, 3));
|
||||||
|
|
||||||
|
// if path is empty we cannot resolve anything
|
||||||
|
if(empty($path_f)) {
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
// get file info from database/cache
|
// get file info from database/cache
|
||||||
$fileInfo = \OC\Files\Filesystem::getFileInfo($path_f);
|
$fileInfo = \OC\Files\Filesystem::getFileInfo($path_f);
|
||||||
|
|
||||||
// if file is encrypted return real file size
|
// if file is encrypted return real file size
|
||||||
if(is_array($fileInfo) && $fileInfo['encrypted'] == 1) {
|
if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
|
||||||
return $fileInfo['unencrypted_size'];
|
$size = $fileInfo['unencrypted_size'];
|
||||||
} else {
|
} else {
|
||||||
|
// self healing if file was removed from file cache
|
||||||
|
if(is_array($fileInfo)) {
|
||||||
|
$userId = \OCP\User::getUser();
|
||||||
|
$util = new Util( $view, $userId );
|
||||||
|
$fixSize = $util->getFileSize($path);
|
||||||
|
if($fixSize > 0) {
|
||||||
|
$size = $fixSize;
|
||||||
|
|
||||||
|
$fileInfo['encrypted'] = true;
|
||||||
|
$fileInfo['unencrypted_size'] = $size;
|
||||||
|
|
||||||
|
// put file info
|
||||||
|
$view->putFileInfo( $path_f, $fileInfo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return $size;
|
return $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function handleFile($path) {
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$view = new \OC_FilesystemView('/');
|
||||||
|
$session = new Session($view);
|
||||||
|
$userId = \OCP\User::getUser();
|
||||||
|
$util = new Util( $view, $userId );
|
||||||
|
|
||||||
|
// Reformat path for use with OC_FSV
|
||||||
|
$path_split = explode( '/', $path );
|
||||||
|
$path_f = implode( '/', array_slice( $path_split, 3 ) );
|
||||||
|
|
||||||
|
// only if file is on 'files' folder fix file size and sharing
|
||||||
|
if($path_split[2] == 'files' && $util->fixFileSize($path)) {
|
||||||
|
|
||||||
|
// get sharing app state
|
||||||
|
$sharingEnabled = \OCP\Share::isEnabled();
|
||||||
|
|
||||||
|
// get users
|
||||||
|
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path_f);
|
||||||
|
|
||||||
|
// update sharing-keys
|
||||||
|
$util->setSharedFileKeyfiles($session, $usersSharing, $path_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Session {
|
||||||
*
|
*
|
||||||
* The ownCloud key pair is used to allow public link sharing even if encryption is enabled
|
* The ownCloud key pair is used to allow public link sharing even if encryption is enabled
|
||||||
*/
|
*/
|
||||||
public function __construct( \OC_FilesystemView $view ) {
|
public function __construct( $view ) {
|
||||||
|
|
||||||
$this->view = $view;
|
$this->view = $view;
|
||||||
|
|
||||||
|
@ -43,16 +43,25 @@ class Session {
|
||||||
if ( ! $this->view->is_dir( 'owncloud_private_key' ) ) {
|
if ( ! $this->view->is_dir( 'owncloud_private_key' ) ) {
|
||||||
|
|
||||||
$this->view->mkdir( 'owncloud_private_key' );
|
$this->view->mkdir( 'owncloud_private_key' );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
|
||||||
|
|
||||||
|
if ($publicShareKeyId === null) {
|
||||||
|
$publicShareKeyId = 'pubShare_'.substr(md5(time()),0,8);
|
||||||
|
\OC_Appconfig::setValue('files_encryption', 'publicShareKeyId', $publicShareKeyId);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
! $this->view->file_exists("/public-keys/owncloud.public.key")
|
! $this->view->file_exists( "/public-keys/".$publicShareKeyId.".public.key" )
|
||||||
|| ! $this->view->file_exists("/owncloud_private_key/owncloud.private.key" )
|
|| ! $this->view->file_exists( "/owncloud_private_key/".$publicShareKeyId.".private.key" )
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$keypair = Crypt::createKeypair();
|
$keypair = Crypt::createKeypair();
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Save public key
|
// Save public key
|
||||||
|
@ -61,17 +70,29 @@ class Session {
|
||||||
$view->mkdir('/public-keys');
|
$view->mkdir('/public-keys');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->file_put_contents( '/public-keys/owncloud.public.key', $keypair['publicKey'] );
|
$this->view->file_put_contents( '/public-keys/'.$publicShareKeyId.'.public.key', $keypair['publicKey'] );
|
||||||
|
|
||||||
// Encrypt private key empthy passphrase
|
// Encrypt private key empthy passphrase
|
||||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
|
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
|
||||||
|
|
||||||
// Save private key
|
// Save private key
|
||||||
$this->view->file_put_contents( '/owncloud_private_key/owncloud.private.key', $encryptedPrivateKey );
|
$this->view->file_put_contents( '/owncloud_private_key/'.$publicShareKeyId.'.private.key', $encryptedPrivateKey );
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = true;
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(\OCP\USER::getUser() === false) {
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/'.$publicShareKeyId.'.private.key' );
|
||||||
|
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' );
|
||||||
|
$this->setPrivateKey($privateKey);
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -73,19 +73,19 @@ class Stream {
|
||||||
|
|
||||||
public function stream_open( $path, $mode, $options, &$opened_path ) {
|
public function stream_open( $path, $mode, $options, &$opened_path ) {
|
||||||
|
|
||||||
$this->userId = \OCP\User::getUser();
|
|
||||||
|
|
||||||
if ( ! isset( $this->rootView ) ) {
|
if ( ! isset( $this->rootView ) ) {
|
||||||
|
|
||||||
$this->rootView = new \OC_FilesystemView( '/' );
|
$this->rootView = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$util = new Util( $this->rootView, \OCP\USER::getUser());
|
||||||
|
|
||||||
|
$this->userId = $util->getUserId();
|
||||||
|
|
||||||
// Strip identifier text from path, this gives us the path relative to data/<user>/files
|
// Strip identifier text from path, this gives us the path relative to data/<user>/files
|
||||||
$this->relPath = str_replace( 'crypt://', '', $path );
|
$this->relPath = \OC\Files\Filesystem::normalizePath(str_replace( 'crypt://', '', $path ));
|
||||||
|
|
||||||
// rawPath is relative to the data directory
|
// rawPath is relative to the data directory
|
||||||
$this->rawPath = $this->userId . '/files/' . $this->relPath;
|
$this->rawPath = $util->getUserFilesDir() . $this->relPath;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
dirname( $this->rawPath ) == 'streams'
|
dirname( $this->rawPath ) == 'streams'
|
||||||
|
@ -298,7 +298,8 @@ class Stream {
|
||||||
// automatically attempted when the file is written to disk -
|
// automatically attempted when the file is written to disk -
|
||||||
// we are handling that separately here and we don't want to
|
// we are handling that separately here and we don't want to
|
||||||
// get into an infinite loop
|
// get into an infinite loop
|
||||||
//\OC_FileProxy::$enabled = false;
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Get the length of the unencrypted data that we are handling
|
// Get the length of the unencrypted data that we are handling
|
||||||
$length = strlen( $data );
|
$length = strlen( $data );
|
||||||
|
@ -322,30 +323,7 @@ class Stream {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch user's public key
|
|
||||||
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
|
|
||||||
|
|
||||||
// Check if OC sharing api is enabled
|
|
||||||
$sharingEnabled = \OCP\Share::isEnabled();
|
|
||||||
|
|
||||||
$util = new Util( $this->rootView, $this->userId );
|
|
||||||
|
|
||||||
// Get all users sharing the file includes current user
|
|
||||||
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId);
|
|
||||||
|
|
||||||
// Fetch public keys for all sharing users
|
|
||||||
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
|
|
||||||
|
|
||||||
// Encrypt enc key for all sharing users
|
|
||||||
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
|
|
||||||
|
|
||||||
$view = new \OC_FilesystemView( '/' );
|
|
||||||
|
|
||||||
// Save the new encrypted file key
|
|
||||||
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] );
|
|
||||||
|
|
||||||
// Save the sharekeys
|
|
||||||
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
|
|
||||||
|
|
||||||
// If extra data is left over from the last round, make sure it
|
// If extra data is left over from the last round, make sure it
|
||||||
// is integrated into the next 6126 / 8192 block
|
// is integrated into the next 6126 / 8192 block
|
||||||
|
@ -437,6 +415,8 @@ class Stream {
|
||||||
$this->size = max( $this->size, $pointer + $length );
|
$this->size = max( $this->size, $pointer + $length );
|
||||||
$this->unencryptedSize += $length;
|
$this->unencryptedSize += $length;
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
return $length;
|
return $length;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -498,8 +478,53 @@ class Stream {
|
||||||
if (
|
if (
|
||||||
$this->meta['mode']!='r'
|
$this->meta['mode']!='r'
|
||||||
and $this->meta['mode']!='rb'
|
and $this->meta['mode']!='rb'
|
||||||
|
and $this->size > 0
|
||||||
) {
|
) {
|
||||||
\OC\Files\Filesystem::putFileInfo( $this->relPath, array( 'encrypted' => 1, 'size' => $this->size, 'unencrypted_size' => $this->unencryptedSize ), '' );
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
// Fetch user's public key
|
||||||
|
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
|
||||||
|
|
||||||
|
// Check if OC sharing api is enabled
|
||||||
|
$sharingEnabled = \OCP\Share::isEnabled();
|
||||||
|
|
||||||
|
$util = new Util( $this->rootView, $this->userId );
|
||||||
|
|
||||||
|
// Get all users sharing the file includes current user
|
||||||
|
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId);
|
||||||
|
|
||||||
|
// Fetch public keys for all sharing users
|
||||||
|
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
|
||||||
|
|
||||||
|
// Encrypt enc key for all sharing users
|
||||||
|
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
|
||||||
|
|
||||||
|
$view = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
|
// Save the new encrypted file key
|
||||||
|
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] );
|
||||||
|
|
||||||
|
// Save the sharekeys
|
||||||
|
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
|
||||||
|
|
||||||
|
// get file info
|
||||||
|
$fileInfo = $view->getFileInfo($this->rawPath);
|
||||||
|
if(!is_array($fileInfo)) {
|
||||||
|
$fileInfo = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable proxy - our work is done
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
// set encryption data
|
||||||
|
$fileInfo['encrypted'] = true;
|
||||||
|
$fileInfo['size'] = $this->size;
|
||||||
|
$fileInfo['unencrypted_size'] = $this->unencryptedSize;
|
||||||
|
|
||||||
|
// set fileinfo
|
||||||
|
$view->putFileInfo( $this->rawPath, $fileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fclose( $this->handle );
|
return fclose( $this->handle );
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* ownCloud
|
* ownCloud
|
||||||
*
|
*
|
||||||
* @author Sam Tuke, Frank Karlitschek
|
* @author Sam Tuke, Frank Karlitschek
|
||||||
* @copyright 2012 Sam Tuke samtuke@owncloud.com,
|
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>,
|
||||||
* Frank Karlitschek frank@owncloud.org
|
* Frank Karlitschek <frank@owncloud.org>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
@ -24,11 +24,8 @@
|
||||||
# Bugs
|
# Bugs
|
||||||
# ----
|
# ----
|
||||||
# Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer
|
# Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer
|
||||||
# Timeouts on first login due to encryption of very large files (fix in progress, as a result streaming is currently broken)
|
|
||||||
# Sharing all files to admin for recovery purposes still in progress
|
# Sharing all files to admin for recovery purposes still in progress
|
||||||
# Possibly public links are broken (not tested since last merge of master)
|
# Possibly public links are broken (not tested since last merge of master)
|
||||||
# encryptAll during login mangles paths: /files/files/
|
|
||||||
# encryptAll is accessing files via encryption proxy - perhaps proxies should be disabled?
|
|
||||||
|
|
||||||
|
|
||||||
# Missing features
|
# Missing features
|
||||||
|
@ -91,20 +88,13 @@ class Util {
|
||||||
|
|
||||||
//// TODO: add support for optional recovery in case of lost passphrase / keys
|
//// TODO: add support for optional recovery in case of lost passphrase / keys
|
||||||
//// TODO: add admin optional required long passphrase for users
|
//// TODO: add admin optional required long passphrase for users
|
||||||
//// TODO: add UI buttons for encrypt / decrypt everything
|
|
||||||
//// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
//// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
||||||
|
|
||||||
|
|
||||||
// Sharing:
|
|
||||||
|
|
||||||
//// TODO: add support for encrypting to multiple public keys
|
|
||||||
//// TODO: add support for decrypting to multiple private keys
|
|
||||||
|
|
||||||
|
|
||||||
// Integration testing:
|
// Integration testing:
|
||||||
|
|
||||||
//// TODO: test new encryption with versioning
|
//// TODO: test new encryption with versioning
|
||||||
//// TODO: test new encryption with sharing
|
//// DONE: test new encryption with sharing
|
||||||
//// TODO: test new encryption with proxies
|
//// TODO: test new encryption with proxies
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,12 +108,39 @@ class Util {
|
||||||
private $shareKeysPath; // Dir containing env keys for shared files
|
private $shareKeysPath; // Dir containing env keys for shared files
|
||||||
private $publicKeyPath; // Path to user's public key
|
private $publicKeyPath; // Path to user's public key
|
||||||
private $privateKeyPath; // Path to user's private key
|
private $privateKeyPath; // Path to user's private key
|
||||||
|
private $publicShareKeyId;
|
||||||
|
private $recoveryKeyId;
|
||||||
|
private $isPublic;
|
||||||
|
|
||||||
public function __construct( \OC_FilesystemView $view, $userId, $client = false ) {
|
public function __construct( \OC_FilesystemView $view, $userId, $client = false ) {
|
||||||
|
|
||||||
$this->view = $view;
|
$this->view = $view;
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
|
$this->isPublic = false;
|
||||||
|
|
||||||
|
$this->publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
|
||||||
|
$this->recoveryKeyId = \OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
|
||||||
|
|
||||||
|
// if we are anonymous/public
|
||||||
|
if($this->userId === false) {
|
||||||
|
$this->userId = $this->publicShareKeyId;
|
||||||
|
|
||||||
|
// only handle for files_sharing app
|
||||||
|
if($GLOBALS['app'] === 'files_sharing') {
|
||||||
|
$this->userDir = '/' . $GLOBALS['fileOwner'];
|
||||||
|
$this->fileFolderName = 'files';
|
||||||
|
$this->userFilesDir = '/' . $GLOBALS['fileOwner'] . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
|
||||||
|
$this->publicKeyDir = '/' . 'public-keys';
|
||||||
|
$this->encryptionDir = '/' . $GLOBALS['fileOwner'] . '/' . 'files_encryption';
|
||||||
|
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
|
||||||
|
$this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
|
||||||
|
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
|
||||||
|
$this->privateKeyPath = '/owncloud_private_key/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
|
||||||
|
$this->isPublic = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
$this->userDir = '/' . $this->userId;
|
$this->userDir = '/' . $this->userId;
|
||||||
$this->fileFolderName = 'files';
|
$this->fileFolderName = 'files';
|
||||||
$this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
|
$this->userFilesDir = '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable?
|
||||||
|
@ -133,7 +150,7 @@ class Util {
|
||||||
$this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
|
$this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
|
||||||
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
|
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
|
||||||
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
|
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ready() {
|
public function ready() {
|
||||||
|
@ -207,17 +224,32 @@ class Util {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there's no record for this user's encryption preferences
|
||||||
|
if ( false === $this->recoveryEnabledForUser() ) {
|
||||||
|
|
||||||
|
// create database configuration
|
||||||
|
$sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery`) VALUES (?,?,?)';
|
||||||
|
$args = array( $this->userId, 'server-side', 0);
|
||||||
|
$query = \OCP\DB::prepare( $sql );
|
||||||
|
$query->execute( $args );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPublicShareKeyId() {
|
||||||
|
return $this->publicShareKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check whether pwd recovery is enabled for a given user
|
* @brief Check whether pwd recovery is enabled for a given user
|
||||||
* @return bool
|
* @return 1 = yes, 0 = no, false = no record
|
||||||
* @note If records are not being returned, check for a hidden space
|
* @note If records are not being returned, check for a hidden space
|
||||||
* at the start of the uid in db
|
* at the start of the uid in db
|
||||||
*/
|
*/
|
||||||
public function recoveryEnabled() {
|
public function recoveryEnabledForUser() {
|
||||||
|
|
||||||
$sql = 'SELECT
|
$sql = 'SELECT
|
||||||
recovery
|
recovery
|
||||||
|
@ -232,16 +264,25 @@ class Util {
|
||||||
|
|
||||||
$result = $query->execute( $args );
|
$result = $query->execute( $args );
|
||||||
|
|
||||||
// Set default in case no records found
|
$recoveryEnabled = array();
|
||||||
$recoveryEnabled = 0;
|
|
||||||
|
|
||||||
while( $row = $result->fetchRow() ) {
|
while( $row = $result->fetchRow() ) {
|
||||||
|
|
||||||
$recoveryEnabled = $row['recovery'];
|
$recoveryEnabled[] = $row['recovery'];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $recoveryEnabled;
|
// If no record is found
|
||||||
|
if ( empty( $recoveryEnabled ) ) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If a record is found
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return $recoveryEnabled[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +291,21 @@ class Util {
|
||||||
* @param bool $enabled Whether to enable or disable recovery
|
* @param bool $enabled Whether to enable or disable recovery
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setRecovery( $enabled ) {
|
public function setRecoveryForUser( $enabled ) {
|
||||||
|
|
||||||
|
$recoveryStatus = $this->recoveryEnabledForUser();
|
||||||
|
|
||||||
|
// If a record for this user already exists, update it
|
||||||
|
if ( false === $recoveryStatus ) {
|
||||||
|
|
||||||
|
$sql = 'INSERT INTO `*PREFIX*encryption`
|
||||||
|
(`uid`,`mode`,`recovery`)
|
||||||
|
VALUES (?,?,?)';
|
||||||
|
|
||||||
|
$args = array( $this->userId, 'server-side', $enabled );
|
||||||
|
|
||||||
|
// Create a new record instead
|
||||||
|
} else {
|
||||||
|
|
||||||
$sql = 'UPDATE
|
$sql = 'UPDATE
|
||||||
*PREFIX*encryption
|
*PREFIX*encryption
|
||||||
|
@ -259,11 +314,10 @@ class Util {
|
||||||
WHERE
|
WHERE
|
||||||
uid = ?';
|
uid = ?';
|
||||||
|
|
||||||
// Ensure value is an integer
|
|
||||||
$enabled = intval( $enabled );
|
|
||||||
|
|
||||||
$args = array( $enabled, $this->userId );
|
$args = array( $enabled, $this->userId );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$query = \OCP\DB::prepare( $sql );
|
$query = \OCP\DB::prepare( $sql );
|
||||||
|
|
||||||
if ( $query->execute( $args ) ) {
|
if ( $query->execute( $args ) ) {
|
||||||
|
@ -282,7 +336,6 @@ class Util {
|
||||||
* @brief Find all files and their encryption status within a directory
|
* @brief Find all files and their encryption status within a directory
|
||||||
* @param string $directory The path of the parent directory to search
|
* @param string $directory The path of the parent directory to search
|
||||||
* @return mixed false if 0 found, array on success. Keys: name, path
|
* @return mixed false if 0 found, array on success. Keys: name, path
|
||||||
|
|
||||||
* @note $directory needs to be a path relative to OC data dir. e.g.
|
* @note $directory needs to be a path relative to OC data dir. e.g.
|
||||||
* /admin/files NOT /backup OR /home/www/oc/data/admin/files
|
* /admin/files NOT /backup OR /home/www/oc/data/admin/files
|
||||||
*/
|
*/
|
||||||
|
@ -427,20 +480,117 @@ class Util {
|
||||||
*/
|
*/
|
||||||
public function isEncryptedPath( $path ) {
|
public function isEncryptedPath( $path ) {
|
||||||
|
|
||||||
// Disable encryption proxy so data retreived is in its
|
// Disable encryption proxy so data retrieved is in its
|
||||||
// original form
|
// original form
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
$data = $this->view->file_get_contents( $path );
|
// we only need 24 byte from the last chunk
|
||||||
|
$data = '';
|
||||||
|
$handle = $this->view->fopen( $path, 'r' );
|
||||||
|
if(!fseek($handle, -24, SEEK_END)) {
|
||||||
|
$data = fgets($handle);
|
||||||
|
}
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = true;
|
// re-enable proxy
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
return Crypt::isCatfileContent( $data );
|
return Crypt::isCatfileContent( $data );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the file size of the unencrypted file
|
||||||
|
* @param $path absolute path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function getFileSize( $path ) {
|
||||||
|
|
||||||
|
$result = 0;
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
// Reformat path for use with OC_FSV
|
||||||
|
$pathSplit = explode( '/', $path );
|
||||||
|
$pathRelative = implode( '/', array_slice( $pathSplit, 3 ) );
|
||||||
|
|
||||||
|
if ($pathSplit[2] == 'files' && $this->view->file_exists($path) && $this->isEncryptedPath($path)) {
|
||||||
|
|
||||||
|
// get the size from filesystem
|
||||||
|
$fullPath = $this->view->getLocalFile($path);
|
||||||
|
$size = filesize($fullPath);
|
||||||
|
|
||||||
|
// calculate last chunk nr
|
||||||
|
$lastChunckNr = floor($size / 8192);
|
||||||
|
|
||||||
|
// open stream
|
||||||
|
$stream = fopen('crypt://' . $pathRelative, "r");
|
||||||
|
|
||||||
|
if(is_resource($stream)) {
|
||||||
|
// calculate last chunk position
|
||||||
|
$lastChunckPos = ($lastChunckNr * 8192);
|
||||||
|
|
||||||
|
// seek to end
|
||||||
|
fseek($stream, $lastChunckPos);
|
||||||
|
|
||||||
|
// get the content of the last chunk
|
||||||
|
$lastChunkContent = fread($stream, 8192);
|
||||||
|
|
||||||
|
// calc the real file size with the size of the last chunk
|
||||||
|
$realSize = (($lastChunckNr * 6126) + strlen($lastChunkContent));
|
||||||
|
|
||||||
|
// store file size
|
||||||
|
$result = $realSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief fix the file size of the encrypted file
|
||||||
|
* @param $path absolute path
|
||||||
|
* @return true / false if file is encrypted
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function fixFileSize( $path ) {
|
||||||
|
|
||||||
|
$result = false;
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$realSize = $this->getFileSize( $path );
|
||||||
|
|
||||||
|
if ( $realSize > 0 ) {
|
||||||
|
|
||||||
|
$cached = $this->view->getFileInfo( $path );
|
||||||
|
$cached['encrypted'] = true;
|
||||||
|
|
||||||
|
// set the size
|
||||||
|
$cached['unencrypted_size'] = $realSize;
|
||||||
|
|
||||||
|
// put file info
|
||||||
|
$this->view->putFileInfo( $path, $cached );
|
||||||
|
|
||||||
|
$result = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Format a path to be relative to the /user/files/ directory
|
* @brief Format a path to be relative to the /user/files/ directory
|
||||||
|
* @note e.g. turns '/admin/files/test.txt' into 'test.txt'
|
||||||
*/
|
*/
|
||||||
public function stripUserFilesPath( $path ) {
|
public function stripUserFilesPath( $path ) {
|
||||||
|
|
||||||
|
@ -453,6 +603,21 @@ class Util {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Format a path to be relative to the /user directory
|
||||||
|
* @note e.g. turns '/admin/files/test.txt' into 'files/test.txt'
|
||||||
|
*/
|
||||||
|
public function stripFilesPath( $path ) {
|
||||||
|
|
||||||
|
$trimmed = ltrim( $path, '/' );
|
||||||
|
$split = explode( '/', $trimmed );
|
||||||
|
$sliced = array_slice( $split, 1 );
|
||||||
|
$relPath = implode( '/', $sliced );
|
||||||
|
|
||||||
|
return $relPath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Format a shared path to be relative to the /user/files/ directory
|
* @brief Format a shared path to be relative to the /user/files/ directory
|
||||||
* @note Expects a path like /uid/files/Shared/filepath
|
* @note Expects a path like /uid/files/Shared/filepath
|
||||||
|
@ -665,8 +830,9 @@ class Util {
|
||||||
// Check that the user is encryption capable, or is the
|
// Check that the user is encryption capable, or is the
|
||||||
// public system user 'ownCloud' (for public shares)
|
// public system user 'ownCloud' (for public shares)
|
||||||
if (
|
if (
|
||||||
$util->ready()
|
$user == $this->publicShareKeyId
|
||||||
or $user == 'owncloud'
|
or $user == $this->recoveryKeyId
|
||||||
|
or $util->ready()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Construct array of ready UIDs for Keymanager{}
|
// Construct array of ready UIDs for Keymanager{}
|
||||||
|
@ -738,25 +904,27 @@ class Util {
|
||||||
* @brief Encrypt keyfile to multiple users
|
* @brief Encrypt keyfile to multiple users
|
||||||
* @param array $users list of users which should be able to access the file
|
* @param array $users list of users which should be able to access the file
|
||||||
* @param string $filePath path of the file to be shared
|
* @param string $filePath path of the file to be shared
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setSharedFileKeyfiles( Session $session, array $users, $filePath ) {
|
public function setSharedFileKeyfiles( Session $session, array $users, $filePath ) {
|
||||||
|
|
||||||
// Make sure users are capable of sharing
|
// Make sure users are capable of sharing
|
||||||
$filteredUids = $this->filterShareReadyUsers( $users );
|
$filteredUids = $this->filterShareReadyUsers( $users );
|
||||||
|
|
||||||
// trigger_error( print_r($filteredUids, 1) );
|
// If we're attempting to share to unready users
|
||||||
|
|
||||||
if ( ! empty( $filteredUids['unready'] ) ) {
|
if ( ! empty( $filteredUids['unready'] ) ) {
|
||||||
|
|
||||||
// Notify user of unready userDir
|
\OC_Log::write( 'Encryption library', 'Sharing to these user(s) failed as they are unready for encryption:"'.print_r( $filteredUids['unready'], 1 ), \OC_Log::WARN );
|
||||||
// TODO: Move this out of here; it belongs somewhere else
|
|
||||||
\OCP\JSON::error();
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get public keys for each user, ready for generating sharekeys
|
// Get public keys for each user, ready for generating sharekeys
|
||||||
$userPubKeys = Keymanager::getPublicKeys( $this->view, $filteredUids['ready'] );
|
$userPubKeys = Keymanager::getPublicKeys( $this->view, $filteredUids['ready'] );
|
||||||
|
|
||||||
|
// Note proxy status then disable it
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Get the current users's private key for decrypting existing keyfile
|
// Get the current users's private key for decrypting existing keyfile
|
||||||
|
@ -772,21 +940,19 @@ class Util {
|
||||||
|
|
||||||
// Save the recrypted key to it's owner's keyfiles directory
|
// Save the recrypted key to it's owner's keyfiles directory
|
||||||
// Save new sharekeys to all necessary user directory
|
// Save new sharekeys to all necessary user directory
|
||||||
// TODO: Reuse the keyfile, it it exists, instead of making a new one
|
|
||||||
if (
|
if (
|
||||||
! Keymanager::setFileKey( $this->view, $filePath, $fileOwner, $multiEncKey['data'] )
|
! Keymanager::setFileKey( $this->view, $filePath, $fileOwner, $multiEncKey['data'] )
|
||||||
|| ! Keymanager::setShareKeys( $this->view, $filePath, $multiEncKey['keys'] )
|
|| ! Keymanager::setShareKeys( $this->view, $filePath, $multiEncKey['keys'] )
|
||||||
) {
|
) {
|
||||||
|
|
||||||
trigger_error( "SET Share keys failed" );
|
\OC_Log::write( 'Encryption library', 'Keyfiles could not be saved for users sharing ' . $filePath, \OC_Log::ERROR );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete existing keyfile
|
// Return proxy to original status
|
||||||
// Do this last to ensure file is recoverable in case of error
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
// Keymanager::deleteFileKey( $this->view, $this->userId, $params['fileTarget'] );
|
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -798,7 +964,18 @@ class Util {
|
||||||
public function getSharingUsersArray( $sharingEnabled, $filePath, $currentUserId = false ) {
|
public function getSharingUsersArray( $sharingEnabled, $filePath, $currentUserId = false ) {
|
||||||
|
|
||||||
// Check if key recovery is enabled
|
// Check if key recovery is enabled
|
||||||
$recoveryEnabled = $this->recoveryEnabled();
|
if (
|
||||||
|
\OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' )
|
||||||
|
&& $this->recoveryEnabledForUser()
|
||||||
|
) {
|
||||||
|
|
||||||
|
$recoveryEnabled = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$recoveryEnabled = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure that a share key is generated for the owner too
|
// Make sure that a share key is generated for the owner too
|
||||||
list( $owner, $ownerPath ) = $this->getUidAndFilename( $filePath );
|
list( $owner, $ownerPath ) = $this->getUidAndFilename( $filePath );
|
||||||
|
@ -806,7 +983,11 @@ class Util {
|
||||||
if ( $sharingEnabled ) {
|
if ( $sharingEnabled ) {
|
||||||
|
|
||||||
// Find out who, if anyone, is sharing the file
|
// Find out who, if anyone, is sharing the file
|
||||||
$userIds = \OCP\Share::getUsersSharingFile( $ownerPath, $owner,true, true, true );
|
$result = \OCP\Share::getUsersSharingFile( $ownerPath, $owner,true, true, true );
|
||||||
|
$userIds = $result['users'];
|
||||||
|
if ( $result['public'] ) {
|
||||||
|
$userIds[] = $this->publicShareKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,16 +995,19 @@ class Util {
|
||||||
// Admin UID to list of users to share to
|
// Admin UID to list of users to share to
|
||||||
if ( $recoveryEnabled ) {
|
if ( $recoveryEnabled ) {
|
||||||
|
|
||||||
// FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB?
|
// Find recoveryAdmin user ID
|
||||||
$adminUid = 'recoveryAdmin';
|
$recoveryKeyId = \OC_Appconfig::getValue( 'files_encryption', 'recoveryKeyId' );
|
||||||
|
|
||||||
$userIds[] = $adminUid;
|
// Add recoveryAdmin to list of users sharing
|
||||||
|
$userIds[] = $recoveryKeyId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add current user if given
|
// add current user if given
|
||||||
if ( $currentUserId != false ) {
|
if ( $currentUserId != false ) {
|
||||||
|
|
||||||
$userIds[] = $currentUserId;
|
$userIds[] = $currentUserId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove duplicate UIDs
|
// Remove duplicate UIDs
|
||||||
|
@ -833,6 +1017,78 @@ class Util {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set file migration status for user
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setMigrationStatus( $status ) {
|
||||||
|
|
||||||
|
$sql = 'UPDATE
|
||||||
|
*PREFIX*encryption
|
||||||
|
SET
|
||||||
|
migrationStatus = ?
|
||||||
|
WHERE
|
||||||
|
uid = ?';
|
||||||
|
|
||||||
|
$args = array( $status, $this->userId );
|
||||||
|
|
||||||
|
$query = \OCP\DB::prepare( $sql );
|
||||||
|
|
||||||
|
if ( $query->execute( $args ) ) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether pwd recovery is enabled for a given user
|
||||||
|
* @return 1 = yes, 0 = no, false = no record
|
||||||
|
* @note If records are not being returned, check for a hidden space
|
||||||
|
* at the start of the uid in db
|
||||||
|
*/
|
||||||
|
public function getMigrationStatus() {
|
||||||
|
|
||||||
|
$sql = 'SELECT
|
||||||
|
migrationStatus
|
||||||
|
FROM
|
||||||
|
`*PREFIX*encryption`
|
||||||
|
WHERE
|
||||||
|
uid = ?';
|
||||||
|
|
||||||
|
$args = array( $this->userId );
|
||||||
|
|
||||||
|
$query = \OCP\DB::prepare( $sql );
|
||||||
|
|
||||||
|
$result = $query->execute( $args );
|
||||||
|
|
||||||
|
$migrationStatus = array();
|
||||||
|
|
||||||
|
while( $row = $result->fetchRow() ) {
|
||||||
|
|
||||||
|
$migrationStatus[] = $row['migrationStatus'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no record is found
|
||||||
|
if ( empty( $migrationStatus ) ) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If a record is found
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return $migrationStatus[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief get uid of the owners of the file and the path to the file
|
* @brief get uid of the owners of the file and the path to the file
|
||||||
* @param $path Path of the file to check
|
* @param $path Path of the file to check
|
||||||
|
@ -842,13 +1098,20 @@ class Util {
|
||||||
*/
|
*/
|
||||||
public function getUidAndFilename( $path ) {
|
public function getUidAndFilename( $path ) {
|
||||||
|
|
||||||
$fileOwnerUid = \OC\Files\Filesystem::getOwner( $path );
|
$view = new \OC\Files\View($this->userFilesDir);
|
||||||
|
$fileOwnerUid = $view->getOwner( $path );
|
||||||
|
|
||||||
|
// handle public access
|
||||||
|
if($fileOwnerUid === false && $this->isPublic) {
|
||||||
|
$filename = $path;
|
||||||
|
$fileOwnerUid = $GLOBALS['fileOwner'];
|
||||||
|
|
||||||
|
return array ( $fileOwnerUid, $filename );
|
||||||
|
} else {
|
||||||
|
|
||||||
// Check that UID is valid
|
// Check that UID is valid
|
||||||
if ( ! \OCP\User::userExists( $fileOwnerUid ) ) {
|
if ( ! \OCP\User::userExists( $fileOwnerUid ) ) {
|
||||||
|
|
||||||
throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $path . '"' );
|
throw new \Exception( 'Could not find owner (UID = "' . var_export( $fileOwnerUid, 1 ) . '") of file "' . $path . '"' );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Bah, this dependency should be elsewhere
|
// NOTE: Bah, this dependency should be elsewhere
|
||||||
|
@ -862,7 +1125,7 @@ class Util {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$info = \OC\Files\Filesystem::getFileInfo( $path );
|
$info = $view->getFileInfo( $path );
|
||||||
$ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' );
|
$ownerView = new \OC\Files\View( '/' . $fileOwnerUid . '/files' );
|
||||||
|
|
||||||
// Fetch real file path from DB
|
// Fetch real file path from DB
|
||||||
|
@ -871,7 +1134,7 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make path relative for use by $view
|
// Make path relative for use by $view
|
||||||
$relpath = $fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename;
|
$relpath = \OC\Files\Filesystem::normalizePath($fileOwnerUid . '/' . $this->fileFolderName . '/' . $filename);
|
||||||
|
|
||||||
// Check that the filename we're using is working
|
// Check that the filename we're using is working
|
||||||
if ( $this->view->file_exists( $relpath ) ) {
|
if ( $this->view->file_exists( $relpath ) ) {
|
||||||
|
@ -883,6 +1146,8 @@ class Util {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,18 +1157,129 @@ class Util {
|
||||||
* @return array with list of files relative to the users files folder
|
* @return array with list of files relative to the users files folder
|
||||||
*/
|
*/
|
||||||
public function getAllFiles( $dir ) {
|
public function getAllFiles( $dir ) {
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
|
|
||||||
$content = $this->view->getDirectoryContent( $this->userFilesDir . $dir );
|
$content = $this->view->getDirectoryContent( $this->userFilesDir . $dir );
|
||||||
|
|
||||||
|
// handling for re shared folders
|
||||||
|
$path_split = explode( '/', $dir );
|
||||||
|
$shared = '';
|
||||||
|
|
||||||
|
if( $path_split[1] === 'Shared' ) {
|
||||||
|
|
||||||
|
$shared = '/Shared';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
foreach ( $content as $c ) {
|
foreach ( $content as $c ) {
|
||||||
if ($c['type'] === "dir" ) {
|
|
||||||
$result = array_merge($result, $this->getAllFiles(substr($c['path'],5)));
|
$sharedPart = $path_split[sizeof( $path_split )-1];
|
||||||
|
$targetPathSplit = array_reverse( explode( '/', $c['path'] ) );
|
||||||
|
|
||||||
|
$path = '';
|
||||||
|
|
||||||
|
// rebuild path
|
||||||
|
foreach ( $targetPathSplit as $pathPart ) {
|
||||||
|
|
||||||
|
if ( $pathPart !== $sharedPart ) {
|
||||||
|
|
||||||
|
$path = '/' . $pathPart . $path;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$result[] = substr($c['path'], 5);
|
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
return $result;
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $dir.$path;
|
||||||
|
|
||||||
|
if ($c['type'] === "dir" ) {
|
||||||
|
|
||||||
|
$result = array_merge( $result, $this->getAllFiles( $path ) );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$result[] = $path;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get shares parent.
|
||||||
|
* @param int $id of the current share
|
||||||
|
* @return array of the parent
|
||||||
|
*/
|
||||||
|
public static function getShareParent( $id ) {
|
||||||
|
|
||||||
|
$query = \OC_DB::prepare( 'SELECT `file_target`, `item_type`'
|
||||||
|
.' FROM `*PREFIX*share`'
|
||||||
|
.' WHERE `id` = ?' );
|
||||||
|
|
||||||
|
$result = $query->execute( array( $id ) );
|
||||||
|
|
||||||
|
$row = $result->fetchRow();
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get owner of the shared files.
|
||||||
|
* @param int $Id of a share
|
||||||
|
* @return owner
|
||||||
|
*/
|
||||||
|
public function getOwnerFromSharedFile( $id ) {
|
||||||
|
|
||||||
|
$query = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 );
|
||||||
|
$source = $query->execute( array( $id ) )->fetchRow();
|
||||||
|
|
||||||
|
if ( isset($source['parent'] ) ) {
|
||||||
|
|
||||||
|
$parent = $source['parent'];
|
||||||
|
|
||||||
|
while ( isset( $parent ) ) {
|
||||||
|
|
||||||
|
$query = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 );
|
||||||
|
$item = $query->execute( array( $parent ) )->fetchRow();
|
||||||
|
|
||||||
|
if ( isset( $item['parent'] ) ) {
|
||||||
|
|
||||||
|
$parent = $item['parent'];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$fileOwner = $item['uid_owner'];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$fileOwner = $source['uid_owner'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fileOwner;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserId()
|
||||||
|
{
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserFilesDir()
|
||||||
|
{
|
||||||
|
return $this->userFilesDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,20 +8,22 @@
|
||||||
|
|
||||||
\OC_Util::checkAdminUser();
|
\OC_Util::checkAdminUser();
|
||||||
|
|
||||||
$tmpl = new OCP\Template( 'files_encryption', 'settings' );
|
$tmpl = new OCP\Template( 'files_encryption', 'settings-admin' );
|
||||||
|
|
||||||
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
|
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
|
||||||
|
|
||||||
// Check if an adminRecovery account is enabled for recovering files after lost pwd
|
// Check if an adminRecovery account is enabled for recovering files after lost pwd
|
||||||
$view = new OC_FilesystemView( '' );
|
$view = new OC_FilesystemView( '' );
|
||||||
$util = new \OCA\Encryption\Util( $view, \OCP\USER::getUser() );
|
|
||||||
$recoveryEnabled = $util->recoveryEnabled();
|
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
|
||||||
|
$recoveryAdminUid = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminUid' );
|
||||||
|
|
||||||
$tmpl->assign( 'blacklist', $blackList );
|
$tmpl->assign( 'blacklist', $blackList );
|
||||||
$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) );
|
$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) );
|
||||||
$tmpl->assign( 'recoveryEnabled', $recoveryEnabled );
|
$tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
|
||||||
|
$tmpl->assign( 'recoveryAdminUid', $recoveryAdminUid );
|
||||||
|
|
||||||
\OCP\Util::addscript( 'files_encryption', 'settings' );
|
\OCP\Util::addscript( 'files_encryption', 'settings-admin' );
|
||||||
\OCP\Util::addscript( 'core', 'multiselect' );
|
\OCP\Util::addscript( 'core', 'multiselect' );
|
||||||
|
|
||||||
return $tmpl->fetchPage();
|
return $tmpl->fetchPage();
|
|
@ -6,10 +6,35 @@
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Add CSS stylesheet
|
||||||
|
\OC_Util::addStyle( 'files_encryption', 'settings-personal' );
|
||||||
|
|
||||||
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
|
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
|
||||||
|
|
||||||
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
|
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
|
||||||
|
|
||||||
|
// Add human readable message in case nothing is blacklisted
|
||||||
|
if (
|
||||||
|
1 == count( $blackList )
|
||||||
|
&& $blackList[0] == ''
|
||||||
|
) {
|
||||||
|
|
||||||
|
// FIXME: Make this string translatable
|
||||||
|
$blackList[0] = "(None - all filetypes will be encrypted)";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = \OCP\USER::getUser();
|
||||||
|
$view = new \OC_FilesystemView( '/' );
|
||||||
|
$util = new \OCA\Encryption\Util( $view, $user );
|
||||||
|
|
||||||
|
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
|
||||||
|
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
|
||||||
|
|
||||||
|
\OCP\Util::addscript( 'files_encryption', 'settings-personal' );
|
||||||
|
|
||||||
|
$tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
|
||||||
|
$tmpl->assign( 'recoveryEnabledForUser', $recoveryEnabledForUser );
|
||||||
$tmpl->assign( 'blacklist', $blackList );
|
$tmpl->assign( 'blacklist', $blackList );
|
||||||
|
|
||||||
return $tmpl->fetchPage();
|
return $tmpl->fetchPage();
|
||||||
|
|
|
@ -4,22 +4,16 @@
|
||||||
<p>
|
<p>
|
||||||
<strong><?php p($l->t( 'Encryption' )); ?></strong>
|
<strong><?php p($l->t( 'Encryption' )); ?></strong>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<?php p($l->t( "Exclude the following file types from encryption:" )); ?>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<select
|
|
||||||
id='encryption_blacklist'
|
|
||||||
title="<?php p($l->t( 'None' ))?>"
|
|
||||||
multiple="multiple">
|
|
||||||
<?php foreach($_["blacklist"] as $type): ?>
|
|
||||||
<option selected="selected" value="<?php p($type); ?>"> <?php p($type); ?> </option>
|
|
||||||
<?php endforeach;?>
|
|
||||||
</select>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<?php p($l->t( "Enable encryption passwords recovery account (allow sharing to recovery account):" )); ?>
|
<?php p($l->t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?>
|
||||||
<br />
|
<br />
|
||||||
|
<br />
|
||||||
|
<?php if ( empty( $_['recoveryAdminUid'] ) ): ?>
|
||||||
|
<input type="password" name="recoveryPassword" id="recoveryPassword" />
|
||||||
|
<label for="recoveryPassword">Recovery account password</label>
|
||||||
|
<br />
|
||||||
|
<?php endif; ?>
|
||||||
<input
|
<input
|
||||||
type='radio'
|
type='radio'
|
||||||
name='adminEnableRecovery'
|
name='adminEnableRecovery'
|
|
@ -3,13 +3,17 @@
|
||||||
<legend>
|
<legend>
|
||||||
<?php p( $l->t( 'Encryption' ) ); ?>
|
<?php p( $l->t( 'Encryption' ) ); ?>
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<?php p($l->t( 'File encryption is enabled.' )); ?>
|
<!-- <?php p( $l->t( 'File encryption is enabled.' ) ); ?> -->
|
||||||
</p>
|
</p>
|
||||||
<?php if ( ! empty( $_["blacklist"] ) ): ?>
|
<?php if ( ! empty( $_["blacklist"] ) ): ?>
|
||||||
<p>
|
<p>
|
||||||
|
<strong>File types</strong>
|
||||||
|
<br />
|
||||||
<?php p( $l->t( 'The following file types will not be encrypted:' ) ); ?>
|
<?php p( $l->t( 'The following file types will not be encrypted:' ) ); ?>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<?php foreach( $_["blacklist"] as $type ): ?>
|
<?php foreach( $_["blacklist"] as $type ): ?>
|
||||||
<li>
|
<li>
|
||||||
|
@ -18,5 +22,43 @@
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<br />
|
||||||
|
<?php if ( $_["recoveryEnabled"] ): ?>
|
||||||
|
<p>
|
||||||
|
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery by sharing all files with administrator:" ) ); ?></label>
|
||||||
|
<br />
|
||||||
|
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?></em>
|
||||||
|
<br />
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='userEnableRecovery'
|
||||||
|
value='1'
|
||||||
|
<?php echo ( $_["recoveryEnabledForUser"] == 1 ? 'checked="checked"' : '' ); ?> />
|
||||||
|
<?php p( $l->t( "Enabled" ) ); ?>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='userEnableRecovery'
|
||||||
|
value='0'
|
||||||
|
<?php echo ( $_["recoveryEnabledForUser"] == 0 ? 'checked="checked"' : '' ); ?> />
|
||||||
|
<?php p( $l->t( "Disabled" ) ); ?>
|
||||||
|
<div id="recoveryEnabledSuccess"><?php p( $l->t( 'File recovery settings updated' ) ); ?></div>
|
||||||
|
<div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<label for="encryptAll"><?php p( $l->t( "Scan for unencrypted files and encrypt them" ) ); ?></label>
|
||||||
|
<br />
|
||||||
|
<em><?php p( $l->t( "Use this if you suspect that you still have files which are unencrypted, or encrypted using ownCloud 4 or older." ) ); ?></em>
|
||||||
|
<br />
|
||||||
|
<input type="submit" id="encryptAll" name="encryptAll" value="<?php p( $l->t( 'Scan and encrypt files' ) ); ?>" />
|
||||||
|
<input type="password" name="userPassword" id="userPassword" />
|
||||||
|
<label for="encryptAll"><?php p( $l->t( "Account password" ) ); ?></label>
|
||||||
|
<div id="encryptAllSuccess"><?php p( $l->t( 'Scan complete' ) );?></div>
|
||||||
|
<div id="encryptAllError"><?php p( $l->t( 'Unable to scan and encrypt files' ) );?></div>
|
||||||
|
</p>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -15,13 +15,13 @@ require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../lib/helper.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
|
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
|
||||||
|
|
||||||
use OCA\Encryption;
|
use OCA\Encryption;
|
||||||
|
|
||||||
// This has to go here because otherwise session errors arise, and the private
|
// This has to go here because otherwise session errors arise, and the private
|
||||||
// encryption key needs to be saved in the session
|
// encryption key needs to be saved in the session
|
||||||
\OC_User::login( 'admin', 'admin' );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @note It would be better to use Mockery here for mocking out the session
|
* @note It would be better to use Mockery here for mocking out the session
|
||||||
|
@ -34,6 +34,9 @@ use OCA\Encryption;
|
||||||
class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
|
// reset backend
|
||||||
|
\OC_User::clearBackends();
|
||||||
|
\OC_User::useBackend('database');
|
||||||
|
|
||||||
// set content for encrypting / decrypting in tests
|
// set content for encrypting / decrypting in tests
|
||||||
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
|
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
|
||||||
|
@ -53,8 +56,23 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
$this->userId = 'admin';
|
$this->userId = 'admin';
|
||||||
$this->pass = 'admin';
|
$this->pass = 'admin';
|
||||||
|
|
||||||
\OC_Filesystem::init( '/' );
|
$userHome = \OC_User::getHome($this->userId);
|
||||||
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
|
$this->dataDir = str_replace('/'.$this->userId, '', $userHome);
|
||||||
|
|
||||||
|
// Filesystem related hooks
|
||||||
|
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||||
|
|
||||||
|
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||||
|
|
||||||
|
\OC_Util::tearDownFS();
|
||||||
|
\OC_User::setUserId('');
|
||||||
|
\OC\Files\Filesystem::setView(false);
|
||||||
|
\OC_Util::setupFS($this->userId);
|
||||||
|
\OC_User::setUserId($this->userId);
|
||||||
|
|
||||||
|
$params['uid'] = $this->userId;
|
||||||
|
$params['password'] = $this->pass;
|
||||||
|
OCA\Encryption\Hooks::login($params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,38 +240,51 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
function testSymmetricStreamEncryptShortFileContent() {
|
function testSymmetricStreamEncryptShortFileContent() {
|
||||||
|
|
||||||
$filename = 'tmp-'.time();
|
$filename = 'tmp-'.time().'.test';
|
||||||
|
|
||||||
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
|
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
|
||||||
|
|
||||||
// Test that data was successfully written
|
// Test that data was successfully written
|
||||||
$this->assertTrue( is_int( $cryptedFile ) );
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
// Get file contents without using any wrapper to get it's actual contents on disk
|
||||||
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
||||||
|
|
||||||
|
// Re-enable proxy - our work is done
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
// Check that the file was encrypted before being written to disk
|
// Check that the file was encrypted before being written to disk
|
||||||
$this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
|
$this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
|
||||||
|
|
||||||
// Get private key
|
// Get the encrypted keyfile
|
||||||
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
|
$encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
|
||||||
|
|
||||||
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
|
// Attempt to fetch the user's shareKey
|
||||||
|
$shareKey = Encryption\Keymanager::getShareKey( $this->view, $this->userId, $filename );
|
||||||
|
|
||||||
|
// get session
|
||||||
|
$session = new Encryption\Session( $this->view );
|
||||||
|
|
||||||
// Get keyfile
|
// get private key
|
||||||
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
|
$privateKey = $session->getPrivateKey( $this->userId );
|
||||||
|
|
||||||
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
|
|
||||||
|
|
||||||
|
// Decrypt keyfile with shareKey
|
||||||
|
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
|
||||||
|
|
||||||
// Manually decrypt
|
// Manually decrypt
|
||||||
$manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile );
|
$manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $retreivedCryptedFile, $plainKeyfile );
|
||||||
|
|
||||||
// Check that decrypted data matches
|
// Check that decrypted data matches
|
||||||
$this->assertEquals( $this->dataShort, $manualDecrypt );
|
$this->assertEquals( $this->dataShort, $manualDecrypt );
|
||||||
|
|
||||||
|
// Teardown
|
||||||
|
$this->view->unlink( $this->userId . '/files/' . $filename );
|
||||||
|
|
||||||
|
Encryption\Keymanager::deleteFileKey( $this->view, $this->userId, $filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -265,7 +296,7 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
function testSymmetricStreamEncryptLongFileContent() {
|
function testSymmetricStreamEncryptLongFileContent() {
|
||||||
|
|
||||||
// Generate a a random filename
|
// Generate a a random filename
|
||||||
$filename = 'tmp-'.time();
|
$filename = 'tmp-'.time().'.test';
|
||||||
|
|
||||||
// Save long data as encrypted file using stream wrapper
|
// Save long data as encrypted file using stream wrapper
|
||||||
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
|
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
|
||||||
|
@ -273,10 +304,16 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
// Test that data was successfully written
|
// Test that data was successfully written
|
||||||
$this->assertTrue( is_int( $cryptedFile ) );
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
// Get file contents without using any wrapper to get it's actual contents on disk
|
||||||
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
||||||
|
|
||||||
// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n";
|
// Re-enable proxy - our work is done
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
|
||||||
// Check that the file was encrypted before being written to disk
|
// Check that the file was encrypted before being written to disk
|
||||||
$this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
|
$this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
|
||||||
|
@ -291,18 +328,20 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
//print_r($e);
|
//print_r($e);
|
||||||
|
|
||||||
|
// Get the encrypted keyfile
|
||||||
|
$encKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
|
||||||
|
|
||||||
// Get private key
|
// Attempt to fetch the user's shareKey
|
||||||
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
|
$shareKey = Encryption\Keymanager::getShareKey( $this->view, $this->userId, $filename );
|
||||||
|
|
||||||
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
|
// get session
|
||||||
|
$session = new Encryption\Session( $this->view );
|
||||||
|
|
||||||
|
// get private key
|
||||||
|
$privateKey = $session->getPrivateKey( $this->userId );
|
||||||
|
|
||||||
// Get keyfile
|
// Decrypt keyfile with shareKey
|
||||||
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
|
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
|
||||||
|
|
||||||
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
|
|
||||||
|
|
||||||
|
|
||||||
// Set var for reassembling decrypted content
|
// Set var for reassembling decrypted content
|
||||||
$decrypt = '';
|
$decrypt = '';
|
||||||
|
@ -310,26 +349,20 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
// Manually decrypt chunk
|
// Manually decrypt chunk
|
||||||
foreach ($e as $e) {
|
foreach ($e as $e) {
|
||||||
|
|
||||||
// echo "\n\$e = $e";
|
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $plainKeyfile );
|
||||||
|
|
||||||
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile );
|
|
||||||
|
|
||||||
// Assemble decrypted chunks
|
// Assemble decrypted chunks
|
||||||
$decrypt .= $chunkDecrypt;
|
$decrypt .= $chunkDecrypt;
|
||||||
|
|
||||||
// echo "\n\$chunkDecrypt = $chunkDecrypt";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// echo "\n\$decrypt = $decrypt";
|
|
||||||
|
|
||||||
$this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
|
$this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
|
||||||
|
|
||||||
// Teardown
|
// Teardown
|
||||||
|
|
||||||
$this->view->unlink( $filename );
|
$this->view->unlink( $this->userId . '/files/' . $filename );
|
||||||
|
|
||||||
Encryption\Keymanager::deleteFileKey( $filename );
|
Encryption\Keymanager::deleteFileKey( $this->view, $this->userId, $filename );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,14 +379,13 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
// Test that data was successfully written
|
// Test that data was successfully written
|
||||||
$this->assertTrue( is_int( $cryptedFile ) );
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
|
||||||
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
|
|
||||||
|
|
||||||
$decrypt = file_get_contents( 'crypt://' . $filename );
|
$decrypt = file_get_contents( 'crypt://' . $filename );
|
||||||
|
|
||||||
$this->assertEquals( $this->dataShort, $decrypt );
|
$this->assertEquals( $this->dataShort, $decrypt );
|
||||||
|
|
||||||
|
// tear down
|
||||||
|
$this->view->unlink( $this->userId . '/files/' . $filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSymmetricStreamDecryptLongFileContent() {
|
function testSymmetricStreamDecryptLongFileContent() {
|
||||||
|
@ -366,14 +398,13 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
// Test that data was successfully written
|
// Test that data was successfully written
|
||||||
$this->assertTrue( is_int( $cryptedFile ) );
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
|
||||||
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
|
|
||||||
|
|
||||||
$decrypt = file_get_contents( 'crypt://' . $filename );
|
$decrypt = file_get_contents( 'crypt://' . $filename );
|
||||||
|
|
||||||
$this->assertEquals( $this->dataLong, $decrypt );
|
$this->assertEquals( $this->dataLong, $decrypt );
|
||||||
|
|
||||||
|
// tear down
|
||||||
|
$this->view->unlink( $this->userId . '/files/' . $filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this test still necessary?
|
// Is this test still necessary?
|
||||||
|
@ -600,6 +631,65 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testRenameFile() {
|
||||||
|
|
||||||
|
$filename = 'tmp-'.time();
|
||||||
|
|
||||||
|
// Save long data as encrypted file using stream wrapper
|
||||||
|
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
|
||||||
|
|
||||||
|
// Test that data was successfully written
|
||||||
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
|
$decrypt = file_get_contents( 'crypt://' . $filename );
|
||||||
|
|
||||||
|
$this->assertEquals( $this->dataLong, $decrypt );
|
||||||
|
|
||||||
|
$newFilename = 'tmp-new-'.time();
|
||||||
|
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||||
|
$view->rename( $filename, $newFilename );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
|
$newDecrypt = file_get_contents( 'crypt://' . $newFilename );
|
||||||
|
|
||||||
|
$this->assertEquals( $this->dataLong, $newDecrypt );
|
||||||
|
|
||||||
|
// tear down
|
||||||
|
$view->unlink( $newFilename );
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMoveFileIntoFolder() {
|
||||||
|
|
||||||
|
$filename = 'tmp-'.time();
|
||||||
|
|
||||||
|
// Save long data as encrypted file using stream wrapper
|
||||||
|
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
|
||||||
|
|
||||||
|
// Test that data was successfully written
|
||||||
|
$this->assertTrue( is_int( $cryptedFile ) );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
|
$decrypt = file_get_contents( 'crypt://' . $filename );
|
||||||
|
|
||||||
|
$this->assertEquals( $this->dataLong, $decrypt );
|
||||||
|
|
||||||
|
$newFolder = '/newfolder1';
|
||||||
|
$newFilename = 'tmp-new-'.time();
|
||||||
|
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||||
|
$view->mkdir($newFolder);
|
||||||
|
$view->rename( $filename, $newFolder . '/' . $newFilename );
|
||||||
|
|
||||||
|
// Get file decrypted contents
|
||||||
|
$newDecrypt = file_get_contents( 'crypt://' . $newFolder . '/' . $newFilename );
|
||||||
|
|
||||||
|
$this->assertEquals( $this->dataLong, $newDecrypt );
|
||||||
|
|
||||||
|
// tear down
|
||||||
|
$view->unlink( $newFolder . '/' . $newFilename );
|
||||||
|
$view->unlink( $newFolder );
|
||||||
|
}
|
||||||
|
|
||||||
// function testEncryption(){
|
// function testEncryption(){
|
||||||
//
|
//
|
||||||
// $key=uniqid();
|
// $key=uniqid();
|
|
@ -13,17 +13,21 @@ require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
|
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../lib/helper.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
|
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
|
||||||
|
|
||||||
use OCA\Encryption;
|
use OCA\Encryption;
|
||||||
|
|
||||||
// This has to go here because otherwise session errors arise, and the private
|
// This has to go here because otherwise session errors arise, and the private
|
||||||
// encryption key needs to be saved in the session
|
// encryption key needs to be saved in the session
|
||||||
\OC_User::login( 'admin', 'admin' );
|
//\OC_User::login( 'admin', 'admin' );
|
||||||
|
|
||||||
class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
|
// reset backend
|
||||||
|
\OC_User::clearBackends();
|
||||||
|
\OC_User::useBackend('database');
|
||||||
|
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
@ -45,9 +49,23 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
$this->userId = 'admin';
|
$this->userId = 'admin';
|
||||||
$this->pass = 'admin';
|
$this->pass = 'admin';
|
||||||
|
|
||||||
\OC_Filesystem::init( '/' );
|
$userHome = \OC_User::getHome($this->userId);
|
||||||
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
|
$this->dataDir = str_replace('/'.$this->userId, '', $userHome);
|
||||||
|
|
||||||
|
// Filesystem related hooks
|
||||||
|
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||||
|
|
||||||
|
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||||
|
|
||||||
|
\OC_Util::tearDownFS();
|
||||||
|
\OC_User::setUserId('');
|
||||||
|
\OC\Files\Filesystem::setView(false);
|
||||||
|
\OC_Util::setupFS($this->userId);
|
||||||
|
\OC_User::setUserId($this->userId);
|
||||||
|
|
||||||
|
$params['uid'] = $this->userId;
|
||||||
|
$params['password'] = $this->pass;
|
||||||
|
OCA\Encryption\Hooks::login($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tearDown(){
|
function tearDown(){
|
||||||
|
@ -60,8 +78,12 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
|
$key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
|
||||||
|
|
||||||
|
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->pass);
|
||||||
|
|
||||||
// Will this length vary? Perhaps we should use a range instead
|
// Will this length vary? Perhaps we should use a range instead
|
||||||
$this->assertEquals( 2296, strlen( $key ) );
|
$this->assertGreaterThan( 27, strlen( $privateKey ) );
|
||||||
|
|
||||||
|
$this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $privateKey, 0, 27 ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +91,7 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId );
|
$key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId );
|
||||||
|
|
||||||
$this->assertEquals( 451, strlen( $key ) );
|
$this->assertGreaterThan( 26, strlen( $key ) );
|
||||||
|
|
||||||
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) );
|
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) );
|
||||||
}
|
}
|
||||||
|
@ -81,11 +103,19 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' );
|
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' );
|
||||||
|
|
||||||
$path = 'unittest-'.time().'txt';
|
$file = 'unittest-'.time().'.txt';
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']);
|
||||||
|
|
||||||
|
// Re-enable proxy - our work is done
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
|
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
|
||||||
|
Encryption\Keymanager::setFileKey( $this->view, $file, $this->userId, $key['key'] );
|
||||||
Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,9 +139,15 @@ class Test_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId );
|
$keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId );
|
||||||
|
|
||||||
$this->assertEquals( 451, strlen( $keys['publicKey'] ) );
|
$this->assertGreaterThan( 26, strlen( $keys['publicKey'] ) );
|
||||||
|
|
||||||
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) );
|
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) );
|
||||||
$this->assertEquals( 2296, strlen( $keys['privateKey'] ) );
|
|
||||||
|
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $keys['privateKey'], $this->pass);
|
||||||
|
|
||||||
|
$this->assertGreaterThan( 27, strlen( $keys['privateKey'] ) );
|
||||||
|
|
||||||
|
$this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $privateKey, 0, 27 ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// <?php
|
<?php
|
||||||
// /**
|
// /**
|
||||||
// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
|
// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
|
||||||
// * This file is licensed under the Affero General Public License version 3 or
|
// * This file is licensed under the Affero General Public License version 3 or
|
|
@ -24,13 +24,15 @@ $loader->register();
|
||||||
use \Mockery as m;
|
use \Mockery as m;
|
||||||
use OCA\Encryption;
|
use OCA\Encryption;
|
||||||
|
|
||||||
\OC_User::login( 'admin', 'admin' );
|
|
||||||
|
|
||||||
class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
|
// reset backend
|
||||||
|
\OC_User::useBackend('database');
|
||||||
|
|
||||||
\OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
|
\OC_User::setUserId( 'admin' );
|
||||||
|
$this->userId = 'admin';
|
||||||
|
$this->pass = 'admin';
|
||||||
|
|
||||||
// set content for encrypting / decrypting in tests
|
// set content for encrypting / decrypting in tests
|
||||||
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
|
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
|
||||||
|
@ -39,9 +41,6 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
|
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
|
||||||
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
|
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
|
||||||
|
|
||||||
$this->userId = 'admin';
|
|
||||||
$this->pass = 'admin';
|
|
||||||
|
|
||||||
$keypair = Encryption\Crypt::createKeypair();
|
$keypair = Encryption\Crypt::createKeypair();
|
||||||
|
|
||||||
$this->genPublicKey = $keypair['publicKey'];
|
$this->genPublicKey = $keypair['publicKey'];
|
||||||
|
@ -55,8 +54,26 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$this->view = new \OC_FilesystemView( '/' );
|
$this->view = new \OC_FilesystemView( '/' );
|
||||||
|
|
||||||
$this->mockView = m::mock('OC_FilesystemView');
|
$userHome = \OC_User::getHome($this->userId);
|
||||||
$this->util = new Encryption\Util( $this->mockView, $this->userId );
|
$this->dataDir = str_replace('/'.$this->userId, '', $userHome);
|
||||||
|
|
||||||
|
// Filesystem related hooks
|
||||||
|
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||||
|
|
||||||
|
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||||
|
|
||||||
|
\OC_Util::tearDownFS();
|
||||||
|
\OC_User::setUserId('');
|
||||||
|
\OC\Files\Filesystem::setView(false);
|
||||||
|
\OC_Util::setupFS($this->userId);
|
||||||
|
\OC_User::setUserId($this->userId);
|
||||||
|
|
||||||
|
$params['uid'] = $this->userId;
|
||||||
|
$params['password'] = $this->pass;
|
||||||
|
OCA\Encryption\Hooks::login($params);
|
||||||
|
|
||||||
|
$mockView = m::mock('OC_FilesystemView');
|
||||||
|
$this->util = new Encryption\Util( $mockView, $this->userId );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +85,9 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief test that paths set during User construction are correct
|
* @brief test that paths set during User construction are correct
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
function testKeyPaths() {
|
function testKeyPaths() {
|
||||||
|
|
||||||
|
@ -90,8 +110,8 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$mockView = m::mock('OC_FilesystemView');
|
$mockView = m::mock('OC_FilesystemView');
|
||||||
|
|
||||||
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false );
|
$mockView->shouldReceive( 'file_exists' )->times(7)->andReturn( false );
|
||||||
$mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true );
|
$mockView->shouldReceive( 'mkdir' )->times(6)->andReturn( true );
|
||||||
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
|
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
|
||||||
|
|
||||||
$util = new Encryption\Util( $mockView, $this->userId );
|
$util = new Encryption\Util( $mockView, $this->userId );
|
||||||
|
@ -107,7 +127,7 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$mockView = m::mock('OC_FilesystemView');
|
$mockView = m::mock('OC_FilesystemView');
|
||||||
|
|
||||||
$mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true );
|
$mockView->shouldReceive( 'file_exists' )->times(8)->andReturn( true );
|
||||||
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
|
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
|
||||||
|
|
||||||
$util = new Encryption\Util( $mockView, $this->userId );
|
$util = new Encryption\Util( $mockView, $this->userId );
|
||||||
|
@ -141,7 +161,7 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$mockView = m::mock('OC_FilesystemView');
|
$mockView = m::mock('OC_FilesystemView');
|
||||||
|
|
||||||
$mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true );
|
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( true );
|
||||||
|
|
||||||
$util = new Encryption\Util( $mockView, $this->userId );
|
$util = new Encryption\Util( $mockView, $this->userId );
|
||||||
|
|
||||||
|
@ -158,32 +178,32 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
$util = new Encryption\Util( $this->view, $this->userId );
|
$util = new Encryption\Util( $this->view, $this->userId );
|
||||||
|
|
||||||
$files = $util->findEncFiles( '/', 'encrypted' );
|
$files = $util->findEncFiles( '/'.$this->userId.'/');
|
||||||
|
|
||||||
var_dump( $files );
|
//var_dump( $files );
|
||||||
|
|
||||||
# TODO: Add more tests here to check that if any of the dirs are
|
# TODO: Add more tests here to check that if any of the dirs are
|
||||||
# then false will be returned. Use strict ordering?
|
# then false will be returned. Use strict ordering?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRecoveryEnabled() {
|
function testRecoveryEnabledForUser() {
|
||||||
|
|
||||||
$util = new Encryption\Util( $this->view, $this->userId );
|
$util = new Encryption\Util( $this->view, $this->userId );
|
||||||
|
|
||||||
// Record the value so we can return it to it's original state later
|
// Record the value so we can return it to it's original state later
|
||||||
$enabled = $util->recoveryEnabled();
|
$enabled = $util->recoveryEnabledForUser();
|
||||||
|
|
||||||
$this->assertTrue( $util->setRecovery( 1 ) );
|
$this->assertTrue( $util->setRecoveryForUser( 1 ) );
|
||||||
|
|
||||||
$this->assertEquals( 1, $util->recoveryEnabled() );
|
$this->assertEquals( 1, $util->recoveryEnabledForUser() );
|
||||||
|
|
||||||
$this->assertTrue( $util->setRecovery( 0 ) );
|
$this->assertTrue( $util->setRecoveryForUser( 0 ) );
|
||||||
|
|
||||||
$this->assertEquals( 0, $util->recoveryEnabled() );
|
$this->assertEquals( 0, $util->recoveryEnabledForUser() );
|
||||||
|
|
||||||
// Return the setting to it's previous state
|
// Return the setting to it's previous state
|
||||||
$this->assertTrue( $util->setRecovery( $enabled ) );
|
$this->assertTrue( $util->setRecoveryForUser( $enabled ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,10 +211,24 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
\OC_User::setUserId( 'admin' );
|
\OC_User::setUserId( 'admin' );
|
||||||
|
|
||||||
$this->util->getUidAndFilename( 'test1.txt' );
|
$filename = 'tmp-'.time().'.test';
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
|
||||||
|
|
||||||
|
// Re-enable proxy - our work is done
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
$util = new Encryption\Util( $this->view, $this->userId );
|
||||||
|
|
||||||
|
list($fileOwnerUid, $file) = $util->getUidAndFilename( $filename );
|
||||||
|
|
||||||
|
$this->assertEquals('admin', $fileOwnerUid);
|
||||||
|
|
||||||
|
$this->assertEquals($file, $filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
|
@ -13,6 +13,5 @@
|
||||||
"{count} files" => "{count} файла",
|
"{count} files" => "{count} файла",
|
||||||
"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
|
"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
|
||||||
"Restore" => "Възтановяване",
|
"Restore" => "Възтановяване",
|
||||||
"Delete" => "Изтриване",
|
"Delete" => "Изтриване"
|
||||||
"Deleted Files" => "Изтрити файлове"
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s",
|
"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s",
|
||||||
"Couldn't restore %s" => "Tidak dapat memulihkan %s",
|
"Couldn't restore %s" => "Tidak dapat memulihkan %s",
|
||||||
"perform restore operation" => "jalankan operasi pemulihan",
|
"perform restore operation" => "jalankan operasi pemulihan",
|
||||||
"Error" => "Galat",
|
"Error" => "kesalahan",
|
||||||
"delete file permanently" => "hapus berkas secara permanen",
|
"delete file permanently" => "hapus berkas secara permanen",
|
||||||
"Delete permanently" => "Hapus secara permanen",
|
"Delete permanently" => "hapus secara permanen",
|
||||||
"Name" => "Nama",
|
"Name" => "Nama",
|
||||||
"Deleted" => "Dihapus",
|
"Deleted" => "Dihapus",
|
||||||
"1 folder" => "1 folder",
|
"1 folder" => "1 map",
|
||||||
"{count} folders" => "{count} folder",
|
"{count} folders" => "{count} map",
|
||||||
"1 file" => "1 berkas",
|
"1 file" => "1 berkas",
|
||||||
"{count} files" => "{count} berkas",
|
"{count} files" => "{count} berkas",
|
||||||
"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!",
|
"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!",
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
<?php $TRANSLATIONS = array(
|
<?php $TRANSLATIONS = array(
|
||||||
"Error" => "Feil",
|
"Error" => "Feil",
|
||||||
"Delete permanently" => "Slett for godt",
|
|
||||||
"Name" => "Namn",
|
"Name" => "Namn",
|
||||||
"1 folder" => "1 mappe",
|
|
||||||
"{count} folders" => "{count} mapper",
|
|
||||||
"1 file" => "1 fil",
|
|
||||||
"{count} files" => "{count} filer",
|
|
||||||
"Delete" => "Slett"
|
"Delete" => "Slett"
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
"Name" => "Nazwa",
|
"Name" => "Nazwa",
|
||||||
"Deleted" => "Usunięte",
|
"Deleted" => "Usunięte",
|
||||||
"1 folder" => "1 folder",
|
"1 folder" => "1 folder",
|
||||||
"{count} folders" => "Ilość folderów: {count}",
|
"{count} folders" => "{count} foldery",
|
||||||
"1 file" => "1 plik",
|
"1 file" => "1 plik",
|
||||||
"{count} files" => "Ilość plików: {count}",
|
"{count} files" => "{count} pliki",
|
||||||
"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!",
|
"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!",
|
||||||
"Restore" => "Przywróć",
|
"Restore" => "Przywróć",
|
||||||
"Delete" => "Usuń",
|
"Delete" => "Usuń",
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
"{count} files" => "{count} ficheiros",
|
"{count} files" => "{count} ficheiros",
|
||||||
"Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!",
|
"Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!",
|
||||||
"Restore" => "Restaurar",
|
"Restore" => "Restaurar",
|
||||||
"Delete" => "Eliminar",
|
"Delete" => "Apagar",
|
||||||
"Deleted Files" => "Ficheiros Apagados"
|
"Deleted Files" => "Ficheiros Apagados"
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php $TRANSLATIONS = array(
|
<?php $TRANSLATIONS = array(
|
||||||
"Error" => "Eroare",
|
"Error" => "Eroare",
|
||||||
"Delete permanently" => "Stergere permanenta",
|
|
||||||
"Name" => "Nume",
|
"Name" => "Nume",
|
||||||
"1 folder" => "1 folder",
|
"1 folder" => "1 folder",
|
||||||
"{count} folders" => "{count} foldare",
|
"{count} folders" => "{count} foldare",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"Error" => "Chyba",
|
"Error" => "Chyba",
|
||||||
"delete file permanently" => "trvalo zmazať súbor",
|
"delete file permanently" => "trvalo zmazať súbor",
|
||||||
"Delete permanently" => "Zmazať trvalo",
|
"Delete permanently" => "Zmazať trvalo",
|
||||||
"Name" => "Názov",
|
"Name" => "Meno",
|
||||||
"Deleted" => "Zmazané",
|
"Deleted" => "Zmazané",
|
||||||
"1 folder" => "1 priečinok",
|
"1 folder" => "1 priečinok",
|
||||||
"{count} folders" => "{count} priečinkov",
|
"{count} folders" => "{count} priečinkov",
|
||||||
|
|
|
@ -29,6 +29,17 @@ class Trashbin {
|
||||||
// unit: percentage; 50% of available disk space/quota
|
// unit: percentage; 50% of available disk space/quota
|
||||||
const DEFAULTMAXSIZE=50;
|
const DEFAULTMAXSIZE=50;
|
||||||
|
|
||||||
|
public static function getUidAndFilename($filename) {
|
||||||
|
$uid = \OC\Files\Filesystem::getOwner($filename);
|
||||||
|
\OC\Files\Filesystem::initMountPoints($uid);
|
||||||
|
if ( $uid != \OCP\User::getUser() ) {
|
||||||
|
$info = \OC\Files\Filesystem::getFileInfo($filename);
|
||||||
|
$ownerView = new \OC\Files\View('/'.$uid.'/files');
|
||||||
|
$filename = $ownerView->getPath($info['fileid']);
|
||||||
|
}
|
||||||
|
return array($uid, $filename);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* move file to the trash bin
|
* move file to the trash bin
|
||||||
*
|
*
|
||||||
|
@ -63,7 +74,11 @@ class Trashbin {
|
||||||
$trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
|
$trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
$sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view);
|
$sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view);
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) {
|
if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) {
|
||||||
$trashbinSize += $sizeOfAddedFiles;
|
$trashbinSize += $sizeOfAddedFiles;
|
||||||
|
@ -110,13 +125,17 @@ class Trashbin {
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
$user = \OCP\User::getUser();
|
$user = \OCP\User::getUser();
|
||||||
if ($view->is_dir('files_versions/' . $file_path)) {
|
$rootView = new \OC\Files\View('/');
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_versions/' . $file_path));
|
|
||||||
$view->rename('files_versions/' . $file_path, 'files_trashbin/versions/' . $filename . '.d' . $timestamp);
|
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
|
||||||
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path)) {
|
|
||||||
|
if ($rootView->is_dir($owner.'/files_versions/' . $ownerPath)) {
|
||||||
|
$size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath));
|
||||||
|
$rootView->rename($owner.'/files_versions/' . $ownerPath, $user.'/files_trashbin/versions/' . $filename . '.d' . $timestamp);
|
||||||
|
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
|
||||||
foreach ($versions as $v) {
|
foreach ($versions as $v) {
|
||||||
$size += $view->filesize('files_versions' . $v['path'] . '.v' . $v['version']);
|
$size += $rootView->filesize($owner.'/files_versions' . $v['path'] . '.v' . $v['version']);
|
||||||
$view->rename('files_versions' . $v['path'] . '.v' . $v['version'], 'files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
|
$rootView->rename($owner.'/files_versions' . $v['path'] . '.v' . $v['version'], $user.'/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,35 +162,38 @@ class Trashbin {
|
||||||
if (\OCP\App::isEnabled('files_encryption')) {
|
if (\OCP\App::isEnabled('files_encryption')) {
|
||||||
|
|
||||||
$user = \OCP\User::getUser();
|
$user = \OCP\User::getUser();
|
||||||
|
$rootView = new \OC\Files\View('/');
|
||||||
|
|
||||||
|
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
|
||||||
|
|
||||||
|
|
||||||
// disable proxy to prevent recursive calls
|
// disable proxy to prevent recursive calls
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
// retain key files
|
// retain key files
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/' . $file_path);
|
$keyfile = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/keyfiles/' . $ownerPath);
|
||||||
|
|
||||||
if ($view->is_dir($keyfile) || $view->file_exists($keyfile . '.key')) {
|
if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) {
|
||||||
$user = \OCP\User::getUser();
|
|
||||||
// move keyfiles
|
// move keyfiles
|
||||||
if ($view->is_dir($keyfile)) {
|
if ($rootView->is_dir($keyfile)) {
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
|
$size += self::calculateSize(new \OC\Files\View($keyfile));
|
||||||
$view->rename($keyfile, 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
|
$rootView->rename($keyfile, $user.'/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
|
||||||
} else {
|
} else {
|
||||||
$size += $view->filesize($keyfile . '.key');
|
$size += $rootView->filesize($keyfile . '.key');
|
||||||
$view->rename($keyfile . '.key', 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
|
$rootView->rename($keyfile . '.key', $user.'/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// retain share keys
|
// retain share keys
|
||||||
$sharekeys = \OC\Files\Filesystem::normalizePath('files_encryption/share-keys/' . $file_path);
|
$sharekeys = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/share-keys/' . $ownerPath);
|
||||||
|
|
||||||
if ($view->is_dir($sharekeys)) {
|
if ($rootView->is_dir($sharekeys)) {
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
|
$size += self::calculateSize(new \OC\Files\View($sharekeys));
|
||||||
$view->rename($sharekeys, 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
|
$rootView->rename($sharekeys, $user.'/files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
|
||||||
} else {
|
} else {
|
||||||
// get local path to share-keys
|
// get local path to share-keys
|
||||||
$localShareKeysPath = $view->getLocalFile($sharekeys);
|
$localShareKeysPath = $rootView->getLocalFile($sharekeys);
|
||||||
|
|
||||||
// handle share-keys
|
// handle share-keys
|
||||||
$matches = glob(preg_quote($localShareKeysPath).'*.shareKey');
|
$matches = glob(preg_quote($localShareKeysPath).'*.shareKey');
|
||||||
|
@ -186,10 +208,10 @@ class Trashbin {
|
||||||
if($pathinfo['basename'] == $ownerShareKey) {
|
if($pathinfo['basename'] == $ownerShareKey) {
|
||||||
|
|
||||||
// calculate size
|
// calculate size
|
||||||
$size += $view->filesize($sharekeys. '.' . $user. '.shareKey');
|
$size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey');
|
||||||
|
|
||||||
// move file
|
// move file
|
||||||
$view->rename($sharekeys. '.' . $user. '.shareKey', 'files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp);
|
$rootView->rename($sharekeys. '.' . $user. '.shareKey', $user.'/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// calculate size
|
// calculate size
|
||||||
|
@ -321,6 +343,12 @@ class Trashbin {
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
$user = \OCP\User::getUser();
|
$user = \OCP\User::getUser();
|
||||||
|
$rootView = new \OC\Files\View('/');
|
||||||
|
|
||||||
|
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
|
||||||
|
|
||||||
|
list($owner, $ownerPath) = self::getUidAndFilename($target);
|
||||||
|
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
$versionedFile = $filename;
|
$versionedFile = $filename;
|
||||||
} else {
|
} else {
|
||||||
|
@ -329,15 +357,15 @@ class Trashbin {
|
||||||
|
|
||||||
if ($view->is_dir('/files_trashbin/versions/'.$file)) {
|
if ($view->is_dir('/files_trashbin/versions/'.$file)) {
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file));
|
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file));
|
||||||
$view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath('files_versions/' . $location . '/' . $filename . $ext));
|
$rootView->rename(\OC\Files\Filesystem::normalizePath($user.'/files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath($owner.'/files_versions/' . $ownerPath));
|
||||||
} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) {
|
} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) {
|
||||||
foreach ($versions as $v) {
|
foreach ($versions as $v) {
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp);
|
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp);
|
||||||
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v);
|
$rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
|
||||||
} else {
|
} else {
|
||||||
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v);
|
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v);
|
||||||
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v);
|
$rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,7 +384,7 @@ class Trashbin {
|
||||||
* @param $file complete path to file
|
* @param $file complete path to file
|
||||||
* @param $filename name of file
|
* @param $filename name of file
|
||||||
* @param $ext file extension in case a file with the same $filename already exists
|
* @param $ext file extension in case a file with the same $filename already exists
|
||||||
* @param $location location if file
|
* @param $location location of file
|
||||||
* @param $timestamp deleteion time
|
* @param $timestamp deleteion time
|
||||||
*
|
*
|
||||||
* @return size of restored encrypted file
|
* @return size of restored encrypted file
|
||||||
|
@ -366,20 +394,25 @@ class Trashbin {
|
||||||
$size = 0;
|
$size = 0;
|
||||||
if (\OCP\App::isEnabled('files_encryption')) {
|
if (\OCP\App::isEnabled('files_encryption')) {
|
||||||
$user = \OCP\User::getUser();
|
$user = \OCP\User::getUser();
|
||||||
|
$rootView = new \OC\Files\View('/');
|
||||||
|
|
||||||
|
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
|
||||||
|
|
||||||
|
list($owner, $ownerPath) = self::getUidAndFilename($target);
|
||||||
|
|
||||||
$path_parts = pathinfo($file);
|
$path_parts = pathinfo($file);
|
||||||
$source_location = $path_parts['dirname'];
|
$source_location = $path_parts['dirname'];
|
||||||
|
|
||||||
if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) {
|
if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) {
|
||||||
if($source_location != '.') {
|
if($source_location != '.') {
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename);
|
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename);
|
||||||
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename);
|
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename);
|
||||||
} else {
|
} else {
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
|
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $filename);
|
||||||
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
|
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $filename);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key');
|
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
|
@ -390,35 +423,36 @@ class Trashbin {
|
||||||
$proxyStatus = \OC_FileProxy::$enabled;
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
if ($view->file_exists($keyfile)) {
|
if ($rootView->file_exists($keyfile)) {
|
||||||
// handle directory
|
// handle directory
|
||||||
if ($view->is_dir($keyfile)) {
|
if ($rootView->is_dir($keyfile)) {
|
||||||
|
|
||||||
// handle keyfiles
|
// handle keyfiles
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
|
$size += self::calculateSize(new \OC\Files\View($keyfile));
|
||||||
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext);
|
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath);
|
||||||
|
|
||||||
// handle share-keys
|
// handle share-keys
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
$sharekey .= '.d' . $timestamp;
|
$sharekey .= '.d' . $timestamp;
|
||||||
}
|
}
|
||||||
$view->rename($sharekey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext);
|
$size += self::calculateSize(new \OC\Files\View($sharekey));
|
||||||
|
$rootView->rename($sharekey, $owner.'/files_encryption/share-keys/' . $ownerPath);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// handle keyfiles
|
// handle keyfiles
|
||||||
$size += $view->filesize($keyfile);
|
$size += $rootView->filesize($keyfile);
|
||||||
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key');
|
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key');
|
||||||
|
|
||||||
// handle share-keys
|
// handle share-keys
|
||||||
$ownerShareKey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey');
|
$ownerShareKey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey');
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
$ownerShareKey .= '.d' . $timestamp;
|
$ownerShareKey .= '.d' . $timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$size += $view->filesize($ownerShareKey);
|
$size += $rootView->filesize($ownerShareKey);
|
||||||
|
|
||||||
// move only owners key
|
// move only owners key
|
||||||
$view->rename($ownerShareKey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext . '.' . $user. '.shareKey');
|
$rootView->rename($ownerShareKey, $owner.'/files_encryption/share-keys/' . $ownerPath . '.' . $user. '.shareKey');
|
||||||
|
|
||||||
// try to re-share if file is shared
|
// try to re-share if file is shared
|
||||||
$filesystemView = new \OC_FilesystemView('/');
|
$filesystemView = new \OC_FilesystemView('/');
|
||||||
|
@ -426,7 +460,7 @@ class Trashbin {
|
||||||
$util = new \OCA\Encryption\Util($filesystemView, $user);
|
$util = new \OCA\Encryption\Util($filesystemView, $user);
|
||||||
|
|
||||||
// fix the file size
|
// fix the file size
|
||||||
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $user . '/files/'. $location. '/' .$filename);
|
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath);
|
||||||
$util->fixFileSize($absolutePath);
|
$util->fixFileSize($absolutePath);
|
||||||
|
|
||||||
// get current sharing state
|
// get current sharing state
|
||||||
|
@ -475,7 +509,25 @@ class Trashbin {
|
||||||
$file = $filename;
|
$file = $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$size += self::deleteVersions($view, $file, $filename, $timestamp);
|
||||||
|
$size += self::deleteEncryptionKeys($view, $file, $filename, $timestamp);
|
||||||
|
|
||||||
|
if ($view->is_dir('/files_trashbin/files/'.$file)) {
|
||||||
|
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
|
||||||
|
} else {
|
||||||
|
$size += $view->filesize('/files_trashbin/files/'.$file);
|
||||||
|
}
|
||||||
|
$view->unlink('/files_trashbin/files/'.$file);
|
||||||
|
$trashbinSize -= $size;
|
||||||
|
self::setTrashbinSize($user, $trashbinSize);
|
||||||
|
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function deleteVersions($view, $file, $filename, $timestamp) {
|
||||||
|
$size = 0;
|
||||||
if ( \OCP\App::isEnabled('files_versions') ) {
|
if ( \OCP\App::isEnabled('files_versions') ) {
|
||||||
|
$user = \OCP\User::getUser();
|
||||||
if ($view->is_dir('files_trashbin/versions/'.$file)) {
|
if ($view->is_dir('files_trashbin/versions/'.$file)) {
|
||||||
$size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
|
$size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
|
||||||
$view->unlink('files_trashbin/versions/'.$file);
|
$view->unlink('files_trashbin/versions/'.$file);
|
||||||
|
@ -491,35 +543,37 @@ class Trashbin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function deleteEncryptionKeys($view, $file, $filename, $timestamp) {
|
||||||
|
$size = 0;
|
||||||
|
if (\OCP\App::isEnabled('files_encryption')) {
|
||||||
|
$user = \OCP\User::getUser();
|
||||||
|
|
||||||
// Take care of encryption keys
|
|
||||||
$parts = pathinfo($file);
|
|
||||||
if ($view->is_dir('/files_trashbin/files/' . $file)) {
|
if ($view->is_dir('/files_trashbin/files/' . $file)) {
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
|
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
|
||||||
|
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
|
||||||
} else {
|
} else {
|
||||||
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key');
|
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key');
|
||||||
|
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename . '.' . $user . '.shareKey');
|
||||||
}
|
}
|
||||||
if ($timestamp) {
|
if ($timestamp) {
|
||||||
$keyfile .= '.d' . $timestamp;
|
$keyfile .= '.d' . $timestamp;
|
||||||
|
$sharekeys .= '.d' . $timestamp;
|
||||||
}
|
}
|
||||||
if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) {
|
if ($view->file_exists($keyfile)) {
|
||||||
if ($view->is_dir($keyfile)) {
|
if ($view->is_dir($keyfile)) {
|
||||||
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
|
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
|
||||||
|
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
|
||||||
} else {
|
} else {
|
||||||
$size += $view->filesize($keyfile);
|
$size += $view->filesize($keyfile);
|
||||||
|
$size += $view->filesize($sharekeys);
|
||||||
}
|
}
|
||||||
$view->unlink($keyfile);
|
$view->unlink($keyfile);
|
||||||
|
$view->unlink($sharekeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($view->is_dir('/files_trashbin/files/'.$file)) {
|
|
||||||
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
|
|
||||||
} else {
|
|
||||||
$size += $view->filesize('/files_trashbin/files/'.$file);
|
|
||||||
}
|
}
|
||||||
$view->unlink('/files_trashbin/files/'.$file);
|
|
||||||
$trashbinSize -= $size;
|
|
||||||
self::setTrashbinSize($user, $trashbinSize);
|
|
||||||
|
|
||||||
return $size;
|
return $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
require_once __DIR__.'/../lib/base.php';
|
require_once __DIR__.'/../lib/base.php';
|
||||||
|
|
||||||
|
OC_App::enable('files_encryption');
|
||||||
OC_App::enable('calendar');
|
OC_App::enable('calendar');
|
||||||
OC_App::enable('contacts');
|
OC_App::enable('contacts');
|
||||||
OC_App::enable('apptemplateadvanced');
|
OC_App::enable('apptemplateadvanced');
|
||||||
|
|
Loading…
Reference in New Issue