Renamed recovery methods in Util{} for clarity Added note about bug causing slow page load and redundant keypair generation recoveryAdmin functionality not yet completetags/v6.0.0alpha2
$return = $doSetup = false; | $return = $doSetup = false; | ||||
// Enable recoveryAdmin | |||||
if ( | if ( | ||||
isset( $_POST['adminEnableRecovery'] ) | isset( $_POST['adminEnableRecovery'] ) | ||||
&& $_POST['adminEnableRecovery'] == 1 | |||||
&& isset( $_POST['recoveryPassword'] ) | |||||
&& ! empty ( $_POST['recoveryPassword'] ) | |||||
&& 1 == $_POST['adminEnableRecovery'] | |||||
// && isset( $_POST['recoveryPassword'] ) | |||||
// && ! empty ( $_POST['recoveryPassword'] ) | |||||
) { | ) { | ||||
// TODO: Let the admin set this themselves | // TODO: Let the admin set this themselves | ||||
// If desired recoveryAdmin UID is already in use | // If desired recoveryAdmin UID is already in use | ||||
if ( ! \OC_User::userExists( $recoveryAdminUid ) ) { | if ( ! \OC_User::userExists( $recoveryAdminUid ) ) { | ||||
// Create new recoveryAdmin user | // Create new recoveryAdmin user | ||||
\OC_User::createUser( $recoveryAdminUid, $_POST['recoveryPassword'] ); | \OC_User::createUser( $recoveryAdminUid, $_POST['recoveryPassword'] ); | ||||
} | } | ||||
// If recoveryAdmin has passed other checks | |||||
// Setup recoveryAdmin user for encryption | |||||
if ( $doSetup ) { | if ( $doSetup ) { | ||||
$view = new \OC_FilesystemView( '/' ); | $view = new \OC_FilesystemView( '/' ); | ||||
$util = new Util( $view, $recoveryAdminUid ); | |||||
$util = new \OCA\Encryption\Util( $view, $recoveryAdminUid ); | |||||
// Ensure recoveryAdmin is ready for encryption (has usable keypair etc.) | // Ensure recoveryAdmin is ready for encryption (has usable keypair etc.) | ||||
$util->setupServerSide( $_POST['recoveryPassword'] ); | $util->setupServerSide( $_POST['recoveryPassword'] ); | ||||
} | } | ||||
// Set recoveryAdmin as enabled | |||||
OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 1 ); | |||||
// Disable recoveryAdmin | |||||
} elseif ( | |||||
isset( $_POST['adminEnableRecovery'] ) | |||||
&& 0 == $_POST['adminEnableRecovery'] | |||||
) { | |||||
// Set recoveryAdmin as enabled | |||||
OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 0 ); | |||||
$return = true; | |||||
} | } | ||||
($return) ? OC_JSON::success() : OC_JSON::error(); | ($return) ? OC_JSON::success() : OC_JSON::error(); |
\OCP\JSON::checkLoggedIn(); | \OCP\JSON::checkLoggedIn(); | ||||
\OCP\JSON::checkAppEnabled( 'files_encryption' ); | \OCP\JSON::checkAppEnabled( 'files_encryption' ); | ||||
\OCP\JSON::callCheck(); | |||||
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 ); | |||||
$result = $util->setRecoveryForUser( $_POST['userEnableRecovery'] ); | |||||
if ( $result ) { | if ( $result ) { | ||||
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 ); | ||||
} | } | ||||
// 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' ); |
/** | /** | ||||
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> | |||||
* 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. | * This file is licensed under the Affero General Public License version 3 or later. | ||||
* See the COPYING-README file. | * See the COPYING-README file. | ||||
*/ | */ | ||||
$( 'input:radio[name="adminEnableRecovery"]' ).change( | $( 'input:radio[name="adminEnableRecovery"]' ).change( | ||||
function() { | function() { | ||||
var foo = $( this ).val(); | |||||
var recoveryStatus = $( this ).val(); | |||||
$.post( | $.post( | ||||
OC.filePath('files_encryption', 'ajax', 'adminrecovery.php') | |||||
, { adminEnableRecovery: foo, recoveryPassword: 'password' } | |||||
OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' ) | |||||
, { adminEnableRecovery: recoveryStatus, recoveryPassword: 'password' } | |||||
, function( data ) { | , function( data ) { | ||||
alert( data ); | alert( data ); | ||||
} | } | ||||
); | ); | ||||
function blackListChange(){ | function blackListChange(){ | ||||
var blackList=$('#encryption_blacklist').val().join(','); | |||||
OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); | |||||
var blackList=$( '#encryption_blacklist' ).val().join( ',' ); | |||||
OC.AppConfig.setValue( 'files_encryption', 'type_blacklist', blackList ); | |||||
} | } | ||||
}) | }) |
/** | |||||
* 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() { | |||||
var recoveryStatus = $( this ).val(); | |||||
$.post( | |||||
OC.filePath( 'files_encryption', 'ajax', 'userrecovery.php' ) | |||||
, { userEnableRecovery: recoveryStatus } | |||||
, function( data ) { | |||||
alert( data ); | |||||
} | |||||
); | |||||
} | |||||
); | |||||
}) |
/** | /** | ||||
* @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 ) { |
* | * | ||||
* 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; | ||||
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' ); | |||||
} | } | ||||
if ( | if ( | ||||
! $this->view->file_exists("/public-keys/owncloud.public.key") | |||||
|| ! $this->view->file_exists("/owncloud_private_key/owncloud.private.key" ) | |||||
! $this->view->file_exists( "/public-keys/owncloud.public.key" ) | |||||
|| ! $this->view->file_exists( "/owncloud_private_key/owncloud.private.key" ) | |||||
) { | ) { | ||||
//FIXME: Bug: for some reason file_exists is returning | |||||
// false in above if statement, and causing new keys | |||||
// to be generated on each page load. At last check | |||||
// our app.php is being executed 18 times per page load | |||||
// , causing 18 new keypairs and huge performance hit. | |||||
$keypair = Crypt::createKeypair(); | $keypair = Crypt::createKeypair(); | ||||
\OC_FileProxy::$enabled = false; | \OC_FileProxy::$enabled = false; |
# 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 | ||||
$this->view->file_put_contents( $this->privateKeyPath, $encryptedPrivateKey ); | $this->view->file_put_contents( $this->privateKeyPath, $encryptedPrivateKey ); | ||||
\OC_FileProxy::$enabled = true; | \OC_FileProxy::$enabled = true; | ||||
// 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 ); | |||||
} | |||||
// 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; | ||||
/** | /** | ||||
* @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 | ||||
$result = $query->execute( $args ); | $result = $query->execute( $args ); | ||||
// Set default in case no records found | |||||
$recoveryEnabled = 0; | |||||
$recoveryEnabled = array(); | |||||
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]; | |||||
} | |||||
} | } | ||||
* @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 ) { | |||||
$sql = 'UPDATE | |||||
*PREFIX*encryption | |||||
SET | |||||
recovery = ? | |||||
WHERE | |||||
uid = ?'; | |||||
$recoveryStatus = $this->recoveryEnabledForUser(); | |||||
// If a record for this user already exists, update it | |||||
if ( false === $recoveryStatus ) { | |||||
// Ensure value is an integer | |||||
$enabled = intval( $enabled ); | |||||
$sql = 'INSERT INTO `*PREFIX*encryption` | |||||
(`uid`,`mode`,`recovery`) | |||||
VALUES (?,?,?)'; | |||||
$args = array( $this->userId, 'server-side', $enabled ); | |||||
$args = array( $enabled, $this->userId ); | |||||
// Create a new record instead | |||||
} else { | |||||
$sql = 'UPDATE | |||||
*PREFIX*encryption | |||||
SET | |||||
recovery = ? | |||||
WHERE | |||||
uid = ?'; | |||||
$args = array( $enabled, $this->userId ); | |||||
} | |||||
$query = \OCP\DB::prepare( $sql ); | $query = \OCP\DB::prepare( $sql ); | ||||
if ( $query->execute( $args ) ) { | if ( $query->execute( $args ) ) { | ||||
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(); | |||||
$recoveryEnabled = $this->recoveryEnabledForUser(); | |||||
// 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); |
\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 ); | |||||
\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(); |
$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 ); | |||||
$recoveryEnabledForUser = $util->recoveryEnabledForUser(); | |||||
\OCP\Util::addscript( 'files_encryption', 'settings-personal' ); | |||||
$tmpl->assign( 'recoveryEnabled', $recoveryEnabledForUser ); | |||||
$tmpl->assign( 'blacklist', $blackList ); | $tmpl->assign( 'blacklist', $blackList ); | ||||
return $tmpl->fetchPage(); | return $tmpl->fetchPage(); |
<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> | ||||
<p> | <p> | ||||
<?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> | ||||
<?php endforeach; ?> | <?php endforeach; ?> | ||||
</ul> | </ul> | ||||
<?php endif; ?> | <?php endif; ?> | ||||
<p> | |||||
<?php p($l->t( "Enable password recovery by sharing all files with administrator:" )); ?> | |||||
<br /> | |||||
<input | |||||
type='radio' | |||||
name='userEnableRecovery' | |||||
value='1' | |||||
<?php echo ( $_["recoveryEnabled"] == 1 ? 'checked="checked"' : '' ); ?> /> | |||||
<?php p($l->t( "Enabled" )); ?> | |||||
<br /> | |||||
<input | |||||
type='radio' | |||||
name='userEnableRecovery' | |||||
value='0' | |||||
<?php echo ( $_["recoveryEnabled"] == 0 ? 'checked="checked"' : '' ); ?> /> | |||||
<?php p($l->t( "Disabled" )); ?> | |||||
</p> | |||||
</fieldset> | </fieldset> | ||||
</form> | </form> |
} | } | ||||
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 ) ); | |||||
} | } | ||||