allow admin to recover users files in case of password lost
This commit is contained in:
parent
67a80e1870
commit
9d1e60325c
|
@ -149,6 +149,8 @@ class Hooks {
|
||||||
// the necessary keys)
|
// the necessary keys)
|
||||||
if (Crypt::mode() == 'server') {
|
if (Crypt::mode() == 'server') {
|
||||||
|
|
||||||
|
if ($params['uid'] == \OCP\User::getUser()) {
|
||||||
|
|
||||||
$view = new \OC_FilesystemView('/');
|
$view = new \OC_FilesystemView('/');
|
||||||
|
|
||||||
$session = new Session($view);
|
$session = new Session($view);
|
||||||
|
@ -166,8 +168,41 @@ class Hooks {
|
||||||
// private key has not changed, only the passphrase
|
// private key has not changed, only the passphrase
|
||||||
// used to decrypt it has changed
|
// used to decrypt it has changed
|
||||||
|
|
||||||
|
|
||||||
|
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
|
||||||
|
|
||||||
|
$user = $params['uid'];
|
||||||
|
$recoveryPassword = $params['recoveryPassword'];
|
||||||
|
$newUserPassword = $params['password'];
|
||||||
|
|
||||||
|
$view = new \OC_FilesystemView('/');
|
||||||
|
|
||||||
|
// make sure that the users home is mounted
|
||||||
|
\OC\Files\Filesystem::initMountPoints($user);
|
||||||
|
|
||||||
|
$keypair = Crypt::createKeypair();
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
// Save public key
|
||||||
|
$view->file_put_contents( '/public-keys/'.$user.'.public.key', $keypair['publicKey'] );
|
||||||
|
|
||||||
|
// Encrypt private key empthy passphrase
|
||||||
|
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $newUserPassword );
|
||||||
|
|
||||||
|
// Save private key
|
||||||
|
$view->file_put_contents( '/'.$user.'/files_encryption/'.$user.'.private.key', $encryptedPrivateKey );
|
||||||
|
|
||||||
|
if ( $recoveryPassword ) { // if recovery key is set we can re-encrypt the key files
|
||||||
|
$util = new Util($view, $user);
|
||||||
|
$util->recoverUsersFiles($recoveryPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -69,7 +69,7 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// change password
|
// change recovery password
|
||||||
|
|
||||||
$('input:password[name="changeRecoveryPassword"]').keyup(function(event) {
|
$('input:password[name="changeRecoveryPassword"]').keyup(function(event) {
|
||||||
var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
|
var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Helper {
|
||||||
public static function registerUserHooks() {
|
public static function registerUserHooks() {
|
||||||
|
|
||||||
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
|
\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_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
|
||||||
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' );
|
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' );
|
||||||
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' );
|
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1351,4 +1351,84 @@ class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief decrypt given file with recovery key and encrypt it again to the owner and his new key
|
||||||
|
* @param type $file
|
||||||
|
* @param type $privateKey recovery key to decrypt the file
|
||||||
|
*/
|
||||||
|
private function recoverFile($file, $privateKey) {
|
||||||
|
|
||||||
|
$sharingEnabled = \OCP\Share::isEnabled();
|
||||||
|
|
||||||
|
// Find out who, if anyone, is sharing the file
|
||||||
|
if ($sharingEnabled) {
|
||||||
|
$result = \OCP\Share::getUsersSharingFile($file, $this->userId, true, true, true);
|
||||||
|
$userIds = $result['users'];
|
||||||
|
$userIds[] = $this->recoveryKeyId;
|
||||||
|
if ($result['public']) {
|
||||||
|
$userIds[] = $this->publicShareKeyId;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$userIds = array($this->userId, $this->recoveryKeyId);
|
||||||
|
}
|
||||||
|
$filteredUids = $this->filterShareReadyUsers($userIds);
|
||||||
|
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
//decrypt file key
|
||||||
|
$encKeyfile = $this->view->file_get_contents($this->keyfilesPath.$file.".key");
|
||||||
|
$shareKey = $this->view->file_get_contents($this->shareKeysPath.$file.".".$this->recoveryKeyId.".shareKey");
|
||||||
|
$plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
|
||||||
|
// encrypt file key again to all users, this time with the new public key for the recovered use
|
||||||
|
$userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']);
|
||||||
|
$multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys);
|
||||||
|
|
||||||
|
// write new keys to filesystem TDOO!
|
||||||
|
$this->view->file_put_contents($this->keyfilesPath.$file.'.key', $multiEncKey['data']);
|
||||||
|
foreach ($multiEncKey['keys'] as $userId => $shareKey) {
|
||||||
|
$shareKeyPath = $this->shareKeysPath.$file.'.'.$userId.'.shareKey';
|
||||||
|
$this->view->file_put_contents($shareKeyPath, $shareKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return proxy to original status
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief collect all files and recover them one by one
|
||||||
|
* @param type $path to look for files keys
|
||||||
|
* @param type $privateKey private recovery key which is used to decrypt the files
|
||||||
|
*/
|
||||||
|
private function recoverAllFiles($path, $privateKey) {
|
||||||
|
$dirContent = $this->view->getDirectoryContent($this->keyfilesPath . $path);
|
||||||
|
foreach ($dirContent as $item) {
|
||||||
|
$filePath = substr($item['path'], 25);
|
||||||
|
if ($item['type'] == 'dir') {
|
||||||
|
$this->addRecoveryKey($filePath . '/', $privateKey);
|
||||||
|
} else {
|
||||||
|
$file = substr($filePath, 0, -4);
|
||||||
|
$this->recoverFile($file, $privateKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief recover users files in case of password lost
|
||||||
|
* @param type $recoveryPassword
|
||||||
|
*/
|
||||||
|
public function recoverUsersFiles($recoveryPassword) {
|
||||||
|
|
||||||
|
// Disable encryption proxy to prevent recursive calls
|
||||||
|
$proxyStatus = \OC_FileProxy::$enabled;
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/'.$this->recoveryKeyId.'.private.key' );
|
||||||
|
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $recoveryPassword );
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = $proxyStatus;
|
||||||
|
|
||||||
|
$this->recoverAllFiles('/', $privateKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,13 +393,14 @@ class OC_User {
|
||||||
* @brief Set password
|
* @brief Set password
|
||||||
* @param $uid The username
|
* @param $uid The username
|
||||||
* @param $password The new password
|
* @param $password The new password
|
||||||
|
* @param $recoveryPassword for the encryption app to reset encryption keys
|
||||||
* @returns true/false
|
* @returns true/false
|
||||||
*
|
*
|
||||||
* Change the password of a user
|
* Change the password of a user
|
||||||
*/
|
*/
|
||||||
public static function setPassword( $uid, $password ) {
|
public static function setPassword( $uid, $password, $recoveryPassword = null ) {
|
||||||
$run = true;
|
$run = true;
|
||||||
OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password ));
|
OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password, "recoveryPassword" => $recoveryPassword ));
|
||||||
|
|
||||||
if( $run ) {
|
if( $run ) {
|
||||||
$success = false;
|
$success = false;
|
||||||
|
@ -412,7 +413,7 @@ class OC_User {
|
||||||
}
|
}
|
||||||
// invalidate all login cookies
|
// invalidate all login cookies
|
||||||
OC_Preferences::deleteApp($uid, 'login_token');
|
OC_Preferences::deleteApp($uid, 'login_token');
|
||||||
OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password ));
|
OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password, "recoveryPassword" => $recoveryPassword ));
|
||||||
return $success;
|
return $success;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
|
@ -8,8 +8,9 @@ OC_JSON::checkLoggedIn();
|
||||||
OC_APP::loadApps();
|
OC_APP::loadApps();
|
||||||
|
|
||||||
$username = isset($_POST["username"]) ? $_POST["username"] : OC_User::getUser();
|
$username = isset($_POST["username"]) ? $_POST["username"] : OC_User::getUser();
|
||||||
$password = isset($_POST["newpassword"]) ? $_POST["newpassword"] : null;
|
$password = isset($_POST["password"]) ? $_POST["password"] : null;
|
||||||
$oldPassword=isset($_POST["oldpassword"])?$_POST["oldpassword"]:'';
|
$oldPassword=isset($_POST["oldpassword"])?$_POST["oldpassword"]:'';
|
||||||
|
$recoveryPassword=isset($_POST["recoveryPassword"])?$_POST["recoveryPassword"]:null;
|
||||||
|
|
||||||
$userstatus = null;
|
$userstatus = null;
|
||||||
if(OC_User::isAdminUser(OC_User::getUser())) {
|
if(OC_User::isAdminUser(OC_User::getUser())) {
|
||||||
|
@ -28,7 +29,7 @@ if(is_null($userstatus)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return Success story
|
// Return Success story
|
||||||
if(!is_null($password) && OC_User::setPassword( $username, $password )) {
|
if(!is_null($password) && OC_User::setPassword( $username, $password, $recoveryPassword )) {
|
||||||
OC_JSON::success(array("data" => array( "username" => $username )));
|
OC_JSON::success(array("data" => array( "username" => $username )));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
|
@ -45,6 +45,8 @@ table:not(.nostyle) { width:100%; }
|
||||||
#rightcontent { padding-left: 1em; }
|
#rightcontent { padding-left: 1em; }
|
||||||
div.quota { float:right; display:block; position:absolute; right:25em; top:-1px; }
|
div.quota { float:right; display:block; position:absolute; right:25em; top:-1px; }
|
||||||
div.quota-select-wrapper { position: relative; }
|
div.quota-select-wrapper { position: relative; }
|
||||||
|
div.recoveryPassword { left:50em; display:block; position:absolute; top:-1px; }
|
||||||
|
input#recoveryPassword {width:15em;}
|
||||||
select.quota { position:absolute; left:0; top:0; width:10em; }
|
select.quota { position:absolute; left:0; top:0; width:10em; }
|
||||||
select.quota-user { position:relative; left:0; top:0; width:10em; }
|
select.quota-user { position:relative; left:0; top:0; width:10em; }
|
||||||
div.quota>span { position:absolute; right:0; white-space:nowrap; top:.7em; color:#888; text-shadow:0 1px 0 #fff; }
|
div.quota>span { position:absolute; right:0; white-space:nowrap; top:.7em; color:#888; text-shadow:0 1px 0 #fff; }
|
||||||
|
|
|
@ -351,9 +351,11 @@ $(document).ready(function () {
|
||||||
input.keypress(function (event) {
|
input.keypress(function (event) {
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
if ($(this).val().length > 0) {
|
if ($(this).val().length > 0) {
|
||||||
|
var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val();
|
||||||
|
console.log("RECOVERY PASSWD: " + recoveryPasswordVal);
|
||||||
$.post(
|
$.post(
|
||||||
OC.filePath('settings', 'ajax', 'changepassword.php'),
|
OC.filePath('settings', 'ajax', 'changepassword.php'),
|
||||||
{username: uid, password: $(this).val()},
|
{username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal},
|
||||||
function (result) {
|
function (result) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,7 +38,7 @@ if($_['passwordChangeSupported']) {
|
||||||
<div id="passwordchanged"><?php echo $l->t('Your password was changed');?></div>
|
<div id="passwordchanged"><?php echo $l->t('Your password was changed');?></div>
|
||||||
<div id="passworderror"><?php echo $l->t('Unable to change your password');?></div>
|
<div id="passworderror"><?php echo $l->t('Unable to change your password');?></div>
|
||||||
<input type="password" id="pass1" name="oldpassword" placeholder="<?php echo $l->t('Current password');?>" />
|
<input type="password" id="pass1" name="oldpassword" placeholder="<?php echo $l->t('Current password');?>" />
|
||||||
<input type="password" id="pass2" name="newpassword"
|
<input type="password" id="pass2" name="password"
|
||||||
placeholder="<?php echo $l->t('New password');?>" data-typetoggle="#personal-show" />
|
placeholder="<?php echo $l->t('New password');?>" data-typetoggle="#personal-show" />
|
||||||
<input type="checkbox" id="personal-show" name="show" /><label for="personal-show"></label>
|
<input type="checkbox" id="personal-show" name="show" /><label for="personal-show"></label>
|
||||||
<input id="passwordbutton" type="submit" value="<?php echo $l->t('Change password');?>" />
|
<input id="passwordbutton" type="submit" value="<?php echo $l->t('Change password');?>" />
|
||||||
|
|
|
@ -29,6 +29,11 @@ $_['subadmingroups'] = array_flip($items);
|
||||||
<?php endforeach;?>
|
<?php endforeach;?>
|
||||||
</select> <input type="submit" value="<?php p($l->t('Create'))?>" />
|
</select> <input type="submit" value="<?php p($l->t('Create'))?>" />
|
||||||
</form>
|
</form>
|
||||||
|
<?php if((bool)$_['recoveryAdminEnabled']): ?>
|
||||||
|
<div class="recoveryPassword">
|
||||||
|
<input id="recoveryPassword" type="password" placeholder="<?php p($l->t('Admin Recovery Password'))?>" />
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="quota">
|
<div class="quota">
|
||||||
<span><?php p($l->t('Default Storage'));?></span>
|
<span><?php p($l->t('Default Storage'));?></span>
|
||||||
<?php if((bool) $_['isadmin']): ?>
|
<?php if((bool) $_['isadmin']): ?>
|
||||||
|
|
|
@ -20,6 +20,8 @@ $users = array();
|
||||||
$groups = array();
|
$groups = array();
|
||||||
|
|
||||||
$isadmin = OC_User::isAdminUser(OC_User::getUser());
|
$isadmin = OC_User::isAdminUser(OC_User::getUser());
|
||||||
|
$recoveryAdminEnabled = OC_App::isEnabled('files_encryption') &&
|
||||||
|
OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
|
||||||
|
|
||||||
if($isadmin) {
|
if($isadmin) {
|
||||||
$accessiblegroups = OC_Group::getGroups();
|
$accessiblegroups = OC_Group::getGroups();
|
||||||
|
@ -77,4 +79,5 @@ $tmpl->assign( 'numofgroups', count($accessiblegroups));
|
||||||
$tmpl->assign( 'quota_preset', $quotaPreset);
|
$tmpl->assign( 'quota_preset', $quotaPreset);
|
||||||
$tmpl->assign( 'default_quota', $defaultQuota);
|
$tmpl->assign( 'default_quota', $defaultQuota);
|
||||||
$tmpl->assign( 'defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined);
|
$tmpl->assign( 'defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined);
|
||||||
|
$tmpl->assign( 'recoveryAdminEnabled', $recoveryAdminEnabled);
|
||||||
$tmpl->printPage();
|
$tmpl->printPage();
|
||||||
|
|
Loading…
Reference in New Issue