From 98c933417583c9a83360a0a3c703867b2c32b940 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 14 Nov 2013 18:29:18 +0100 Subject: [PATCH 01/47] LDAP Wizard: move raw login filter field from advanced tab to login filter tab for consistency --- apps/user_ldap/js/settings.js | 19 +++++++++++++++++++ .../templates/part.wizard-loginfilter.php | 10 ++++++++++ apps/user_ldap/templates/settings.php | 3 --- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index dcaeb70b57..4b219ee208 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -613,6 +613,8 @@ var LdapWizard = { }, toggleRawFilter: function(container, moc, mg, stateVar) { + //moc = multiselect objectclass + //mg = mutliselect groups if($(container).hasClass('invisible')) { $(container).removeClass('invisible'); $(moc).multiselect('disable'); @@ -637,6 +639,22 @@ var LdapWizard = { ); }, + toggleRawLoginFilter: function() { + container = '#rawLoginFilterContainer'; + if($(container).hasClass('invisible')) { + $(container).removeClass('invisible'); + action = 'disable'; + property = 'disabled'; + } else { + $(container).addClass('invisible'); + action = 'enable'; + property = false; + } + $('#ldap_loginfilter_attributes').multiselect(action); + $('#ldap_loginfilter_email').prop('disabled', property); + $('#ldap_loginfilter_username').prop('disabled', property); + }, + toggleRawUserFilter: function() { LdapWizard.toggleRawFilter('#rawUserFilterContainer', '#ldap_userfilter_objectclass', @@ -670,6 +688,7 @@ $(document).ready(function() { $('.lwautosave').change(function() { LdapWizard.save(this); }); $('#toggleRawUserFilter').click(LdapWizard.toggleRawUserFilter); $('#toggleRawGroupFilter').click(LdapWizard.toggleRawGroupFilter); + $('#toggleRawLoginFilter').click(LdapWizard.toggleRawLoginFilter); LdapConfiguration.refreshConfig(); $('.ldap_action_continue').click(function(event) { event.preventDefault(); diff --git a/apps/user_ldap/templates/part.wizard-loginfilter.php b/apps/user_ldap/templates/part.wizard-loginfilter.php index d4a36eb0cb..dc5d61e9f7 100644 --- a/apps/user_ldap/templates/part.wizard-loginfilter.php +++ b/apps/user_ldap/templates/part.wizard-loginfilter.php @@ -28,6 +28,16 @@ name="ldap_loginfilter_attributes">

+

+ +

+

diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index feb5ac6385..3ccc7a860f 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -20,9 +20,6 @@

t('Connection Settings'));?>

-

" />

From ff9ecc8a5197138726eb2a756c70399afb5eb97e Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 19 Nov 2013 23:58:08 +0100 Subject: [PATCH 02/47] LDAP Wizard: proper behaviour for raw filter input. remember the editing mode and compose filter only when desired. --- apps/user_ldap/ajax/wizard.php | 3 + apps/user_ldap/js/settings.js | 139 +++++++++++++++++++++------ apps/user_ldap/lib/configuration.php | 9 ++ apps/user_ldap/lib/wizard.php | 39 ++++++++ 4 files changed, 163 insertions(+), 27 deletions(-) diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php index e580c09786..daad007011 100644 --- a/apps/user_ldap/ajax/wizard.php +++ b/apps/user_ldap/ajax/wizard.php @@ -53,8 +53,11 @@ switch($action) { case 'determineGroupsForGroups': case 'determineAttributes': case 'getUserListFilter': + case 'getLoginFilterMode': case 'getUserLoginFilter': + case 'getUserFilterMode': case 'getGroupFilter': + case 'getGroupFilterMode': case 'countUsers': case 'countGroups': try { diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index 4b219ee208..9d824a9e7e 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -106,7 +106,7 @@ var LdapConfiguration = { clearMappings: function(mappingSubject) { $.post( OC.filePath('user_ldap','ajax','clearMappings.php'), - 'ldap_clear_mapping='+mappingSubject, + 'ldap_clear_mapping='+encodeURIComponent(mappingSubject), function(result) { if(result.status == 'success') { OC.dialogs.info( @@ -129,6 +129,8 @@ var LdapWizard = { saveBlacklist: {}, userFilterGroupSelectState: 'enable', spinner: '', + filterModeAssisted: 0, + filterModeRaw: 1, ajax: function(param, fnOnSuccess, fnOnError) { $.post( @@ -146,10 +148,7 @@ var LdapWizard = { applyChanges: function (result) { for (id in result.changes) { - if(!$.isArray(result.changes[id])) { - //no need to blacklist multiselect - LdapWizard.saveBlacklist[id] = true; - } + LdapWizard.blacklistAdd(id); if(id.indexOf('count') > 0) { $('#'+id).text(result.changes[id]); } else { @@ -181,6 +180,25 @@ var LdapWizard = { } }, + + blacklistAdd: function(id) { + obj = $('#'+id); + if(!(obj[0].hasOwnProperty('multiple') && obj[0]['multiple'] == true)) { + //no need to blacklist multiselect + LdapWizard.saveBlacklist[id] = true; + return true; + } + return false; + }, + + blacklistRemove: function(id) { + if(LdapWizard.saveBlacklist.hasOwnProperty(id)) { + delete LdapWizard.saveBlacklist[id]; + return true; + } + return false; + }, + checkBaseDN: function() { host = $('#ldap_host').val(); port = $('#ldap_port').val(); @@ -189,7 +207,8 @@ var LdapWizard = { if(host && port && user && pass) { param = 'action=guessBaseDN'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.showSpinner('#ldap_base'); $('#ldap_base').prop('disabled', 'disabled'); @@ -217,7 +236,8 @@ var LdapWizard = { if(host && !port) { param = 'action=guessPortAndTLS'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.showSpinner('#ldap_port'); $('#ldap_port').prop('disabled', 'disabled'); @@ -241,6 +261,12 @@ var LdapWizard = { }, composeFilter: function(type) { + subject = type.charAt(0).toUpperCase() + type.substr(1); + if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { + //Raw filter editing, i.e. user defined filter, don't compose + return; + } + if(type == 'user') { action = 'getUserListFilter'; } else if(type == 'login') { @@ -250,7 +276,8 @@ var LdapWizard = { } param = 'action='+action+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.ajax(param, function(result) { @@ -308,7 +335,8 @@ var LdapWizard = { _countThings: function(method) { param = 'action='+method+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.ajax(param, function(result) { @@ -330,7 +358,8 @@ var LdapWizard = { detectGroupMemberAssoc: function() { param = 'action=determineGroupMemberAssoc'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.ajax(param, function(result) { @@ -344,7 +373,8 @@ var LdapWizard = { findAttributes: function() { param = 'action=determineAttributes'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.showSpinner('#ldap_loginfilter_attributes'); LdapWizard.ajax(param, @@ -359,7 +389,9 @@ var LdapWizard = { LdapWizard.hideSpinner('#ldap_loginfilter_attributes'); LdapWizard.applyChanges(result); $('#ldap_loginfilter_attributes').multiselect('refresh'); - $('#ldap_loginfilter_attributes').multiselect('enable'); + if($('#rawLoginFilterContainer').hasClass('invisible')) { + $('#ldap_loginfilter_attributes').multiselect('enable'); + } }, function (result) { //deactivate if no attributes found @@ -375,8 +407,9 @@ var LdapWizard = { if(type != 'Users' && type != 'Groups') { return false; } - param = 'action=determineGroupsFor'+type+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + param = 'action=determineGroupsFor'+encodeURIComponent(type)+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.showSpinner('#'+multisel); LdapWizard.ajax(param, @@ -390,7 +423,11 @@ var LdapWizard = { LdapWizard.hideSpinner('#'+multisel); LdapWizard.applyChanges(result); $('#'+multisel).multiselect('refresh'); - $('#'+multisel).multiselect('enable'); + part = type.slice(0, -1); + if($('#raw' + part + 'FilterContainer').hasClass('invisible')) { + //enable only when raw filter editing is not turned on + $('#'+multisel).multiselect('enable'); + } }, function (result) { LdapWizard.hideSpinner('#'+multisel); @@ -403,8 +440,9 @@ var LdapWizard = { if(type != 'User' && type != 'Group') { return false; } - param = 'action=determine'+type+'ObjectClasses'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); + param = 'action=determine'+encodeURIComponent(type)+'ObjectClasses'+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); LdapWizard.showSpinner('#'+multisel); LdapWizard.ajax(param, @@ -466,15 +504,15 @@ var LdapWizard = { }, initGroupFilter: function() { + LdapWizard.regardFilterMode('Group'); LdapWizard.findObjectClasses('ldap_groupfilter_objectclass', 'Group'); LdapWizard.findAvailableGroups('ldap_groupfilter_groups', 'Groups'); - LdapWizard.composeFilter('group'); LdapWizard.countGroups(); }, initLoginFilter: function() { + LdapWizard.regardFilterMode('Login'); LdapWizard.findAttributes(); - LdapWizard.composeFilter('login'); }, initMultiSelect: function(object, id, caption) { @@ -490,9 +528,9 @@ var LdapWizard = { }, initUserFilter: function() { + LdapWizard.regardFilterMode('User'); LdapWizard.findObjectClasses('ldap_userfilter_objectclass', 'User'); LdapWizard.findAvailableGroups('ldap_userfilter_groups', 'Users'); - LdapWizard.composeFilter('user'); LdapWizard.countUsers(); }, @@ -546,9 +584,36 @@ var LdapWizard = { } }, + regardFilterMode: function(subject) { + param = 'action=get'+encodeURIComponent(subject)+'FilterMode'+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + LdapWizard.ajax(param, + function(result) { + property = 'ldap' + subject + 'FilterMode'; + mode = result.changes[property]; + if(mode == LdapWizard.filterModeRaw + && $('#raw'+subject+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+subject+'Filter'](); + } else if(mode == LdapWizard.filterModeAssisted + && !$('#raw'+subject+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+subject+'Filter'](); + } else { + c = $('#raw'+subject+'FilterContainer').hasClass('invisible'); + } + }, + function (result) { + //on error case get back to default i.e. Assisted + if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+subject+'Filter'](); + } + } + ); + }, + save: function(inputObj) { - if(LdapWizard.saveBlacklist.hasOwnProperty(inputObj.id)) { - delete LdapWizard.saveBlacklist[inputObj.id]; + if(LdapWizard.blacklistRemove(inputObj.id)) { return; } if($(inputObj).is('input[type=checkbox]') @@ -581,8 +646,8 @@ var LdapWizard = { }, _save: function(object, value) { - param = 'cfgkey='+object.id+ - '&cfgval='+value+ + param = 'cfgkey='+encodeURIComponent(object.id)+ + '&cfgval='+encodeURIComponent(value)+ '&action=save'+ '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); @@ -612,7 +677,7 @@ var LdapWizard = { } }, - toggleRawFilter: function(container, moc, mg, stateVar) { + toggleRawFilter: function(container, moc, mg, stateVar, modeKey) { //moc = multiselect objectclass //mg = mutliselect groups if($(container).hasClass('invisible')) { @@ -624,42 +689,62 @@ var LdapWizard = { LdapWizard[stateVar] = 'enable'; } $(mg).multiselect('disable'); + LdapWizard._save({ id: modeKey }, LdapWizard.filterModeRaw); } else { $(container).addClass('invisible'); $(mg).multiselect(LdapWizard[stateVar]); $(moc).multiselect('enable'); + LdapWizard._save({ id: modeKey }, LdapWizard.filterModeAssisted); + if(moc.indexOf('user') >= 0) { + LdapWizard.blacklistRemove('ldap_userlist_filter'); + LdapWizard.composeFilter('user'); + } else { + LdapWizard.blacklistRemove('ldap_group_filter'); + LdapWizard.composeFilter('group'); + } } }, toggleRawGroupFilter: function() { + LdapWizard.blacklistRemove('ldap_group_filter'); LdapWizard.toggleRawFilter('#rawGroupFilterContainer', '#ldap_groupfilter_objectclass', '#ldap_groupfilter_groups', - 'groupFilterGroupSelectState' + 'groupFilterGroupSelectState', + 'ldapGroupFilterMode' ); }, toggleRawLoginFilter: function() { + LdapWizard.blacklistRemove('ldap_login_filter'); container = '#rawLoginFilterContainer'; if($(container).hasClass('invisible')) { $(container).removeClass('invisible'); action = 'disable'; property = 'disabled'; + mode = LdapWizard.filterModeRaw; } else { $(container).addClass('invisible'); action = 'enable'; property = false; + mode = LdapWizard.filterModeAssisted; } $('#ldap_loginfilter_attributes').multiselect(action); $('#ldap_loginfilter_email').prop('disabled', property); $('#ldap_loginfilter_username').prop('disabled', property); + LdapWizard._save({ id: 'ldapLoginFilterMode' }, mode); + if(action == 'enable') { + LdapWizard.composeFilter('login'); + } }, toggleRawUserFilter: function() { + LdapWizard.blacklistRemove('ldap_userlist_filter'); LdapWizard.toggleRawFilter('#rawUserFilterContainer', '#ldap_userfilter_objectclass', '#ldap_userfilter_groups', - 'userFilterGroupSelectState' + 'userFilterGroupSelectState', + 'ldapUserFilterMode' ); } }; diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index e14ed824a7..b85cdfe25d 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -47,12 +47,15 @@ class Configuration { 'ldapUserFilterObjectclass' => null, 'ldapUserFilterGroups' => null, 'ldapUserFilter' => null, + 'ldapUserFilterMode' => null, 'ldapGroupFilter' => null, + 'ldapGroupFilterMode' => null, 'ldapGroupFilterObjectclass' => null, 'ldapGroupFilterGroups' => null, 'ldapGroupDisplayName' => null, 'ldapGroupMemberAssocAttr' => null, 'ldapLoginFilter' => null, + 'ldapLoginFilterMode' => null, 'ldapLoginFilterEmail' => null, 'ldapLoginFilterUsername' => null, 'ldapLoginFilterAttributes' => null, @@ -301,13 +304,16 @@ class Configuration { 'ldap_base_users' => '', 'ldap_base_groups' => '', 'ldap_userlist_filter' => '', + 'ldap_user_filter_mode' => 0, 'ldap_userfilter_objectclass' => '', 'ldap_userfilter_groups' => '', 'ldap_login_filter' => 'uid=%uid', + 'ldap_login_filter_mode' => 0, 'ldap_loginfilter_email' => 0, 'ldap_loginfilter_username' => 1, 'ldap_loginfilter_attributes' => '', 'ldap_group_filter' => '', + 'ldap_group_filter_mode' => 0, 'ldap_groupfilter_objectclass' => '', 'ldap_groupfilter_groups' => '', 'ldap_display_name' => 'displayName', @@ -352,11 +358,14 @@ class Configuration { 'ldap_userfilter_objectclass' => 'ldapUserFilterObjectclass', 'ldap_userfilter_groups' => 'ldapUserFilterGroups', 'ldap_userlist_filter' => 'ldapUserFilter', + 'ldap_user_filter_mode' => 'ldapUserFilterMode', 'ldap_login_filter' => 'ldapLoginFilter', + 'ldap_login_filter_mode' => 'ldapLoginFilterMode', 'ldap_loginfilter_email' => 'ldapLoginFilterEmail', 'ldap_loginfilter_username' => 'ldapLoginFilterUsername', 'ldap_loginfilter_attributes' => 'ldapLoginFilterAttributes', 'ldap_group_filter' => 'ldapGroupFilter', + 'ldap_group_filter_mode' => 'ldapGroupFilterMode', 'ldap_groupfilter_objectclass' => 'ldapGroupFilterObjectclass', 'ldap_groupfilter_groups' => 'ldapGroupFilterGroups', 'ldap_display_name' => 'ldapUserDisplayName', diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php index 0b2a6a540f..aac80f982a 100644 --- a/apps/user_ldap/lib/wizard.php +++ b/apps/user_ldap/lib/wizard.php @@ -38,6 +38,9 @@ class Wizard extends LDAPUtility { const LFILTER_USER_LIST = 3; const LFILTER_GROUP_LIST = 4; + const LFILTER_MODE_ASSISTED = 2; + const LFILTER_MODE_RAW = 1; + const LDAP_NW_TIMEOUT = 4; /** @@ -147,6 +150,42 @@ class Wizard extends LDAPUtility { return $this->result; } + /** + * @brief return the state of the Group Filter Mode + */ + public function getGroupFilterMode() { + $this->getFilterMode('ldapGroupFilterMode'); + return $this->result; + } + + /** + * @brief return the state of the Login Filter Mode + */ + public function getLoginFilterMode() { + $this->getFilterMode('ldapLoginFilterMode'); + return $this->result; + } + + /** + * @brief return the state of the User Filter Mode + */ + public function getUserFilterMode() { + $this->getFilterMode('ldapUserFilterMode'); + return $this->result; + } + + /** + * @brief return the state of the mode of the specified filter + * @param $confkey string, contains the access key of the Configuration + */ + private function getFilterMode($confkey) { + $mode = $this->configuration->$confkey; + if(is_null($mode)) { + $mode = $this->LFILTER_MODE_ASSISTED; + } + $this->result->addChange($confkey, $mode); + } + /** * @brief detects the available LDAP attributes * @returns the instance's WizardResult instance From ce1318c9f109296fcdf7365178a96bc5de82e35a Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 20 Nov 2013 15:56:25 +0100 Subject: [PATCH 03/47] LDAP Wizard: clear the cache on save so changes have immediate effect --- apps/user_ldap/ajax/wizard.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php index daad007011..ad75a38436 100644 --- a/apps/user_ldap/ajax/wizard.php +++ b/apps/user_ldap/ajax/wizard.php @@ -90,6 +90,9 @@ switch($action) { exit; } $configuration->saveConfiguration(); + //clear the cache on save + $connection = new \OCA\user_ldap\lib\Connection($ldapWrapper, $prefix); + $connection->clearCache(); OCP\JSON::success(); break; default: From c5cb4206f53d2a87f3d8e17fd8447dae4dc4a50c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 20 Nov 2013 18:10:56 +0100 Subject: [PATCH 04/47] [wip] make encryption work with public gallery sharing --- apps/files_encryption/lib/helper.php | 34 +++++++++++-- apps/files_encryption/lib/keymanager.php | 12 ++--- apps/files_encryption/lib/proxy.php | 7 +-- apps/files_encryption/lib/stream.php | 6 ++- apps/files_encryption/lib/util.php | 62 +++++++++--------------- 5 files changed, 65 insertions(+), 56 deletions(-) diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 0ac6fcf403..e66a84d909 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -225,10 +225,7 @@ class Helper { * @return bool */ public static function isPublicAccess() { - if (\OCP\USER::getUser() === false - || (isset($_GET['service']) && $_GET['service'] == 'files' - && isset($_GET['t'])) - ) { + if (\OCP\USER::getUser() === false) { return true; } else { return false; @@ -255,6 +252,35 @@ class Helper { return $relPath; } + public static function getUser($path) { + + $user = \OCP\User::getUser(); + + // if we are logged in, than we return the userid + if ($user) { + return $user; + } + + // if no user is logged in we try to access a publically shared files. + // In this case we need to try to get the user from the path + + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + // it is not a file relative to data/user/files + if (count($split) < 2 || $split[1] !== 'files') { + return false; + } + + $user = $split[0]; + + if (\OCP\User::userExists($user)) { + return $user; + } + + return false; + } + /** * @brief get path to the correspondig file in data/user/files if path points * to a version or to a file in cache diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 6dadd12a62..8d3e72b422 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -172,16 +172,14 @@ class Keymanager { /** * @brief retrieve keyfile for an encrypted file * @param \OC_FilesystemView $view - * @param $userId + * @param \OCA\Encryption\Util $util * @param $filePath * @internal param \OCA\Encryption\file $string name * @return string file key or false * @note The keyfile returned is asymmetrically encrypted. Decryption * of the keyfile must be performed by client code */ - public static function getFileKey(\OC_FilesystemView $view, $userId, $filePath) { - - $util = new Util($view, \OCP\User::getUser()); + public static function getFileKey(\OC_FilesystemView $view, $util, $filePath) { list($owner, $filename) = $util->getUidAndFilename($filePath); $filename = Helper::stripPartialFileExtension($filename); @@ -364,21 +362,19 @@ class Keymanager { * @brief retrieve shareKey for an encrypted file * @param \OC_FilesystemView $view * @param string $userId + * @param \OCA\Encryption\Util $util * @param string $filePath * @internal param \OCA\Encryption\file $string name * @return string file key or false * @note The sharekey returned is encrypted. Decryption * of the keyfile must be performed by client code */ - public static function getShareKey(\OC_FilesystemView $view, $userId, $filePath) { + public static function getShareKey(\OC_FilesystemView $view, $userId, $util, $filePath) { // try reusing key file if part file $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - //here we need the currently logged in user, while userId can be a different user - $util = new Util($view, \OCP\User::getUser()); - list($owner, $filename) = $util->getUidAndFilename($filePath); $filename = Helper::stripPartialFileExtension($filename); // in case of system wide mount points the keys are stored directly in the data directory diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 54c3b9caa1..f7253b4591 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -260,7 +260,8 @@ class Proxy extends \OC_FileProxy { $view = new \OC_FilesystemView(''); - $util = new Util($view, \OCP\USER::getUser()); + $userId = Helper::getUser($path); + $util = new Util($view, $userId); // If file is already encrypted, decrypt using crypto protocol if ( @@ -323,7 +324,7 @@ class Proxy extends \OC_FileProxy { $view = new \OC_FilesystemView('/'); - $userId = \OCP\User::getUser(); + $userId = Helper::getUser($path); $util = new Util($view, $userId); // if encryption is no longer enabled or if the files aren't migrated yet @@ -398,7 +399,7 @@ class Proxy extends \OC_FileProxy { $view = new \OC_FilesystemView('/'); $session = new \OCA\Encryption\Session($view); - $userId = \OCP\User::getUser(); + $userId = Helper::getUser($path); $util = new Util($view, $userId); // split the path parts diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 1738955d1a..393c133d76 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -250,12 +250,14 @@ class Stream { // Fetch and decrypt keyfile // Fetch existing keyfile - $this->encKeyfile = Keymanager::getFileKey($this->rootView, $this->userId, $this->relPath); + $userId = Helper::getUser($this->rawPath); + $util = new \OCA\Encryption\Util($this->rootView, $userId); + $this->encKeyfile = Keymanager::getFileKey($this->rootView, $util, $this->relPath); // If a keyfile already exists if ($this->encKeyfile) { - $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $this->relPath); + $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $util, $this->relPath); // if there is no valid private key return false if ($this->privateKey === false) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index f9beb9de67..08c8870408 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -38,7 +38,8 @@ class Util { const MIGRATION_OPEN = 0; // user still needs to be migrated private $view; // OC_FilesystemView object for filesystem operations - private $userId; // ID of the currently logged-in user + private $userId; // ID of the user we use to encrypt/decrypt files + private $ownerId; // ID of the user who accesses the file/folder private $client; // Client side encryption mode flag private $publicKeyDir; // Dir containing all public user keys private $encryptionDir; // Dir containing user's files_encryption @@ -58,51 +59,34 @@ class Util { public function __construct(\OC_FilesystemView $view, $userId, $client = false) { $this->view = $view; - $this->userId = $userId; $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 + $this->userDir = '/' . $userId; + $this->fileFolderName = 'files'; + $this->userFilesDir = + '/' . $userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; + $this->publicKeyPath = + $this->publicKeyDir . '/' . $userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = + $this->encryptionDir . '/' . $userId . '.private.key'; // e.g. data/admin/admin.private.key + // make sure that the owners home is mounted + \OC\Files\Filesystem::initMountPoints($userId); + if (\OCA\Encryption\Helper::isPublicAccess()) { $this->userId = $this->publicShareKeyId; - - // only handle for files_sharing app - if (isset($GLOBALS['app']) && $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; - // make sure that the owners home is mounted - \OC\Files\Filesystem::initMountPoints($GLOBALS['fileOwner']); - } - + $this->ownerId = $userId; + $this->isPublic = true; } else { - $this->userDir = '/' . $this->userId; - $this->fileFolderName = 'files'; - $this->userFilesDir = - '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? - $this->publicKeyDir = '/' . 'public-keys'; - $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; - $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; - $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; - $this->publicKeyPath = - $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key - $this->privateKeyPath = - $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key - // make sure that the owners home is mounted - \OC\Files\Filesystem::initMountPoints($this->userId); + $this->userId = $userId; + $this->ownerId = $userId; + $this->isPublic = false; } } @@ -1338,7 +1322,7 @@ class Util { // handle public access if ($this->isPublic) { $filename = $path; - $fileOwnerUid = $GLOBALS['fileOwner']; + $fileOwnerUid = $this->ownerId; return array( $fileOwnerUid, From e11afd306608798a8ba9138cae70e9ab283c490d Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 20 Nov 2013 22:44:23 +0100 Subject: [PATCH 05/47] fix some getShareKey() and getFileKey() calls --- apps/files_encryption/lib/util.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 08c8870408..ce3d253cc9 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -351,7 +351,7 @@ class Util { // scanning every file like this // will eat server resources :( if ( - Keymanager::getFileKey($this->view, $this->userId, $relPath) + Keymanager::getFileKey($this->view, $this, $relPath) && $isEncryptedPath ) { @@ -1043,10 +1043,10 @@ class Util { private function decryptKeyfile($filePath, $privateKey) { // Get the encrypted keyfile - $encKeyfile = Keymanager::getFileKey($this->view, $this->userId, $filePath); + $encKeyfile = Keymanager::getFileKey($this->view, $this, $filePath); // The file has a shareKey and must use it for decryption - $shareKey = Keymanager::getShareKey($this->view, $this->userId, $filePath); + $shareKey = Keymanager::getShareKey($this->view, $this->userId, $this, $filePath); $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); From b9c18d16fe67f71015d1a233e2716bc7c0812140 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 20 Nov 2013 23:23:23 +0100 Subject: [PATCH 06/47] make sure that we always use the correct user id --- apps/files_encryption/lib/keymanager.php | 18 +++++++++++------- apps/files_encryption/lib/stream.php | 12 +++++++++--- apps/files_encryption/lib/util.php | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 8d3e72b422..b207b1437b 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -125,8 +125,8 @@ class Keymanager { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - //here we need the currently logged in user, while userId can be a different user - $util = new Util($view, \OCP\User::getUser()); + $userId = Helper::getUser($path); + $util = new Util($view, $userId); list($owner, $filename) = $util->getUidAndFilename($path); // in case of system wide mount points the keys are stored directly in the data directory @@ -225,7 +225,8 @@ class Keymanager { $trimmed = ltrim($path, '/'); - $util = new Util($view, \OCP\User::getUser()); + $userId = Helper::getUser($path); + $util = new Util($view, $userId); if($util->isSystemWideMountPoint($path)) { $keyPath = '/files_encryption/keyfiles/' . $trimmed; @@ -322,8 +323,10 @@ class Keymanager { // $shareKeys must be an array with the following format: // [userId] => [encrypted key] - // Here we need the currently logged in user, while userId can be a different user - $util = new Util($view, \OCP\User::getUser()); + + $userId = Helper::getUser($path); + + $util = new Util($view, $userId); list($owner, $filename) = $util->getUidAndFilename($path); @@ -441,8 +444,9 @@ class Keymanager { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - //here we need the currently logged in user, while userId can be a different user - $util = new Util($view, \OCP\User::getUser()); + $userId = Helper::getUser($filePath); + + $util = new Util($view, $userId); list($owner, $filename) = $util->getUidAndFilename($filePath); diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 393c133d76..2497e56e89 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -92,10 +92,14 @@ class Stream { $this->session = new \OCA\Encryption\Session($this->rootView); - $this->privateKey = $this->session->getPrivateKey($this->userId); + $this->privateKey = $this->session->getPrivateKey(); - $util = new Util($this->rootView, \OCP\USER::getUser()); + $userId = Helper::getUser($path); + $util = new Util($this->rootView, $userId); + + // need to get the userId once more from util, because now this can be the + // public share key ID $this->userId = $util->getUserId(); // rawPath is relative to the data directory @@ -509,7 +513,9 @@ class Stream { // Check if OC sharing api is enabled $sharingEnabled = \OCP\Share::isEnabled(); - $util = new Util($this->rootView, $this->userId); + $userId = Helper::getUser($this->rawPath); + + $util = new Util($this->rootView, $userId); // Get all users sharing the file includes current user $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index ce3d253cc9..1e8b852fb3 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -69,7 +69,7 @@ class Util { $this->userFilesDir = '/' . $userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? $this->publicKeyDir = '/' . 'public-keys'; - $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->encryptionDir = '/' . $userId . '/' . 'files_encryption'; $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; $this->publicKeyPath = From 318db64b2d9f724c3209bd6ca97f560840e2cc20 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 20 Nov 2013 23:27:42 +0100 Subject: [PATCH 07/47] adapt tests to the new code --- apps/files_encryption/tests/crypt.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/files_encryption/tests/crypt.php b/apps/files_encryption/tests/crypt.php index 5146613e25..0086371d22 100755 --- a/apps/files_encryption/tests/crypt.php +++ b/apps/files_encryption/tests/crypt.php @@ -157,6 +157,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { $filename = 'tmp-' . time() . '.test'; + $util = new Encryption\Util(new \OC_FilesystemView(), $this->userId); + $cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/'. $filename, $this->dataShort); // Test that data was successfully written @@ -176,10 +178,10 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { $this->assertNotEquals($this->dataShort, $retreivedCryptedFile); // Get the encrypted keyfile - $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename); + $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $util, $filename); // Attempt to fetch the user's shareKey - $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename); + $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $util, $filename); // get session $session = new \OCA\Encryption\Session($this->view); @@ -214,6 +216,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { // Generate a a random filename $filename = 'tmp-' . time() . '.test'; + $util = new Encryption\Util(new \OC_FilesystemView(), $this->userId); + // Save long data as encrypted file using stream wrapper $cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong . $this->dataLong); @@ -244,16 +248,16 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { $i = 0; while ($i < count($r)-1) { $e[] = $r[$i] . $r[$i+1]; - $i = $i + 2; + $i = $i + 2; } //print_r($e); // Get the encrypted keyfile - $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename); + $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $util, $filename); // Attempt to fetch the user's shareKey - $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename); + $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $util, $filename); // get session $session = new \OCA\Encryption\Session($this->view); @@ -387,7 +391,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { * @brief test decryption using legacy blowfish method */ function testLegacyDecryptShort() { - + $crypted = $this->legacyEncrypt($this->dataShort, $this->pass); $decrypted = Encryption\Crypt::legacyBlockDecrypt($crypted, $this->pass); @@ -401,7 +405,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { * @brief test decryption using legacy blowfish method */ function testLegacyDecryptLong() { - + $crypted = $this->legacyEncrypt($this->dataLong, $this->pass); $decrypted = Encryption\Crypt::legacyBlockDecrypt($crypted, $this->pass); @@ -653,8 +657,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { // tear down $view->unlink($filename); } - - + + /** * @brief encryption using legacy blowfish method * @param $data string data to encrypt From b27fc42e1f0fbd1edebb1eb1818de4b4e0c4ee4b Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 21 Nov 2013 00:23:38 +0100 Subject: [PATCH 08/47] public upload now also works with encryption enabled --- apps/files/index.php | 1 - apps/files_encryption/lib/keymanager.php | 14 +++++--------- apps/files_encryption/lib/proxy.php | 7 +++---- apps/files_encryption/lib/stream.php | 14 +++++++------- apps/files_encryption/lib/util.php | 4 ++-- apps/files_encryption/tests/keymanager.php | 4 +++- apps/files_sharing/public.php | 3 --- 7 files changed, 20 insertions(+), 27 deletions(-) diff --git a/apps/files/index.php b/apps/files/index.php index 9ae378d7a1..8f6838aa0d 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -108,7 +108,6 @@ if ($needUpgrade) { // if the encryption app is disabled, than everything is fine (INIT_SUCCESSFUL status code) $encryptionInitStatus = 2; if (OC_App::isEnabled('files_encryption')) { - $publicUploadEnabled = 'no'; $session = new \OCA\Encryption\Session(new \OC\Files\View('/')); $encryptionInitStatus = $session->getInitialized(); } diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index b207b1437b..b4396864a4 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -112,6 +112,7 @@ class Keymanager { * @brief store file encryption key * * @param \OC_FilesystemView $view + * @param \OCA\Encryption\Util $util * @param string $path relative path of the file, including filename * @param $userId * @param $catfile @@ -120,13 +121,11 @@ class Keymanager { * @note The keyfile is not encrypted here. Client code must * asymmetrically encrypt the keyfile before passing it to this method */ - public static function setFileKey(\OC_FilesystemView $view, $path, $userId, $catfile) { + public static function setFileKey(\OC_FilesystemView $view, $util, $path, $userId, $catfile) { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - $userId = Helper::getUser($path); - $util = new Util($view, $userId); list($owner, $filename) = $util->getUidAndFilename($path); // in case of system wide mount points the keys are stored directly in the data directory @@ -315,19 +314,16 @@ class Keymanager { /** * @brief store multiple share keys for a single file * @param \OC_FilesystemView $view - * @param $path + * @param \OCA\Encryption\Util $util + * @param string $path * @param array $shareKeys * @return bool */ - public static function setShareKeys(\OC_FilesystemView $view, $path, array $shareKeys) { + public static function setShareKeys(\OC_FilesystemView $view, $util, $path, array $shareKeys) { // $shareKeys must be an array with the following format: // [userId] => [encrypted key] - $userId = Helper::getUser($path); - - $util = new Util($view, $userId); - list($owner, $filename) = $util->getUidAndFilename($path); // in case of system wide mount points the keys are stored directly in the data directory diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index f7253b4591..43d451d67c 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -47,8 +47,10 @@ class Proxy extends \OC_FileProxy { */ private static function shouldEncrypt($path) { + $userId = Helper::getUser($path); + if (\OCP\App::isEnabled('files_encryption') === false || Crypt::mode() !== 'server' || - strpos($path, '/' . \OCP\User::getUser() . '/files') !== 0) { + strpos($path, '/' . $userId . '/files') !== 0) { return false; } @@ -244,9 +246,6 @@ class Proxy extends \OC_FileProxy { // split the path parts $pathParts = explode('/', $path); - // get relative path - $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); - // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted if (isset($pathParts[2]) && $pathParts[2] === 'cache') { return $result; diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 2497e56e89..3fbcf7db3e 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -90,11 +90,14 @@ class Stream { $this->rootView = new \OC_FilesystemView('/'); } + // rawPath is relative to the data directory + $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + $this->session = new \OCA\Encryption\Session($this->rootView); $this->privateKey = $this->session->getPrivateKey(); - $userId = Helper::getUser($path); + $userId = Helper::getUser($this->rawPath); $util = new Util($this->rootView, $userId); @@ -102,9 +105,6 @@ class Stream { // public share key ID $this->userId = $util->getUserId(); - // rawPath is relative to the data directory - $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); - // Strip identifier text from path, this gives us the path relative to data//files $this->relPath = Helper::stripUserFilesPath($this->rawPath); // if raw path doesn't point to a real file, check if it is a version or a file in the trash bin @@ -518,7 +518,7 @@ class Stream { $util = new Util($this->rootView, $userId); // Get all users sharing the file includes current user - $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId); + $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $userId); $checkedUserIds = $util->filterShareReadyUsers($uniqueUserIds); // Fetch public keys for all sharing users @@ -528,10 +528,10 @@ class Stream { $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys); // Save the new encrypted file key - Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']); + Keymanager::setFileKey($this->rootView, $util, $this->relPath, $userId, $this->encKeyfiles['data']); // Save the sharekeys - Keymanager::setShareKeys($this->rootView, $this->relPath, $this->encKeyfiles['keys']); + Keymanager::setShareKeys($this->rootView, $util, $this->relPath, $this->encKeyfiles['keys']); // Re-enable proxy - our work is done \OC_FileProxy::$enabled = $proxyStatus; diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 1e8b852fb3..b15c61f599 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -1097,8 +1097,8 @@ class Util { // Save the recrypted key to it's owner's keyfiles directory // Save new sharekeys to all necessary user directory if ( - !Keymanager::setFileKey($this->view, $filePath, $fileOwner, $multiEncKey['data']) - || !Keymanager::setShareKeys($this->view, $filePath, $multiEncKey['keys']) + !Keymanager::setFileKey($this->view, $this, $filePath, $fileOwner, $multiEncKey['data']) + || !Keymanager::setShareKeys($this->view, $this, $filePath, $multiEncKey['keys']) ) { \OCP\Util::writeLog('Encryption library', diff --git a/apps/files_encryption/tests/keymanager.php b/apps/files_encryption/tests/keymanager.php index ad6bbd3a7e..72ee270ee5 100644 --- a/apps/files_encryption/tests/keymanager.php +++ b/apps/files_encryption/tests/keymanager.php @@ -145,13 +145,15 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { $file = 'unittest-' . time() . '.txt'; + $util = new Encryption\Util($this->view, $this->userId); + // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $this->view->file_put_contents($this->userId . '/files/' . $file, $this->dataShort); - Encryption\Keymanager::setFileKey($this->view, $file, $this->userId, $key); + Encryption\Keymanager::setFileKey($this->view, $util, $file, $this->userId, $key); $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/keyfiles/' . $file . '.key')); diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index d59f9b7401..f809196701 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -152,9 +152,6 @@ if (isset($path)) { $tmpl->assign('sharingToken', $token); $tmpl->assign('disableSharing', true); $allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE); - if (\OCP\App::isEnabled('files_encryption')) { - $allowPublicUploadEnabled = false; - } if (OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes') === 'no') { $allowPublicUploadEnabled = false; } From 2b361ea085812a7b97102d026c421905549b5142 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 21 Nov 2013 10:09:07 +0100 Subject: [PATCH 09/47] better distinction between userID and keyId --- apps/files_encryption/lib/stream.php | 30 ++++++++++++-------------- apps/files_encryption/lib/util.php | 32 +++++++++++++++++----------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 2497e56e89..409c6ff627 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -55,6 +55,7 @@ class Stream { private $rawPath; // The raw path relative to the data dir private $relPath; // rel path to users file dir private $userId; + private $keyId; private $handle; // Resource returned by fopen private $meta = array(); // Header / meta for source stream private $writeCache; @@ -94,17 +95,17 @@ class Stream { $this->privateKey = $this->session->getPrivateKey(); - $userId = Helper::getUser($path); - - $util = new Util($this->rootView, $userId); - - // need to get the userId once more from util, because now this can be the - // public share key ID - $this->userId = $util->getUserId(); - // rawPath is relative to the data directory $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + $this->userId = Helper::getUser($this->rawPath); + + $util = new Util($this->rootView, $this->userId); + + // get the key ID which we want to use, canm be the users key or the + // public share key + $this->keyId = $util->getKeyId(); + // Strip identifier text from path, this gives us the path relative to data//files $this->relPath = Helper::stripUserFilesPath($this->rawPath); // if raw path doesn't point to a real file, check if it is a version or a file in the trash bin @@ -254,14 +255,13 @@ class Stream { // Fetch and decrypt keyfile // Fetch existing keyfile - $userId = Helper::getUser($this->rawPath); - $util = new \OCA\Encryption\Util($this->rootView, $userId); + $util = new \OCA\Encryption\Util($this->rootView, $this->userId); $this->encKeyfile = Keymanager::getFileKey($this->rootView, $util, $this->relPath); // If a keyfile already exists if ($this->encKeyfile) { - $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $util, $this->relPath); + $shareKey = Keymanager::getShareKey($this->rootView, $this->keyId, $util, $this->relPath); // if there is no valid private key return false if ($this->privateKey === false) { @@ -508,14 +508,12 @@ class Stream { \OC_FileProxy::$enabled = false; // Fetch user's public key - $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->userId); + $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->keyId); // Check if OC sharing api is enabled $sharingEnabled = \OCP\Share::isEnabled(); - $userId = Helper::getUser($this->rawPath); - - $util = new Util($this->rootView, $userId); + $util = new Util($this->rootView, $this->userId); // Get all users sharing the file includes current user $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId); @@ -528,7 +526,7 @@ class Stream { $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys); // Save the new encrypted file key - Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']); + Keymanager::setFileKey($this->rootView, $this->relPath, $this->keyId, $this->encKeyfiles['data']); // Save the sharekeys Keymanager::setShareKeys($this->rootView, $this->relPath, $this->encKeyfiles['keys']); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 1e8b852fb3..2dd4fd9c16 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -39,7 +39,7 @@ class Util { private $view; // OC_FilesystemView object for filesystem operations private $userId; // ID of the user we use to encrypt/decrypt files - private $ownerId; // ID of the user who accesses the file/folder + private $keyId; // ID of the key we want to manipulate private $client; // Client side encryption mode flag private $publicKeyDir; // Dir containing all public user keys private $encryptionDir; // Dir containing user's files_encryption @@ -60,32 +60,31 @@ class Util { $this->view = $view; $this->client = $client; + $this->userId = $userId; $this->publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); $this->recoveryKeyId = \OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); - $this->userDir = '/' . $userId; + $this->userDir = '/' . $this->userId; $this->fileFolderName = 'files'; $this->userFilesDir = '/' . $userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? $this->publicKeyDir = '/' . 'public-keys'; - $this->encryptionDir = '/' . $userId . '/' . 'files_encryption'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; $this->publicKeyPath = - $this->publicKeyDir . '/' . $userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key $this->privateKeyPath = - $this->encryptionDir . '/' . $userId . '.private.key'; // e.g. data/admin/admin.private.key + $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key // make sure that the owners home is mounted \OC\Files\Filesystem::initMountPoints($userId); if (\OCA\Encryption\Helper::isPublicAccess()) { - $this->userId = $this->publicShareKeyId; - $this->ownerId = $userId; + $this->keyId = $this->publicShareKeyId; $this->isPublic = true; } else { - $this->userId = $userId; - $this->ownerId = $userId; + $this->keyId = $this->userId; $this->isPublic = false; } } @@ -172,13 +171,13 @@ class Util { // check if public-key exists but private-key is missing if ($this->view->file_exists($this->publicKeyPath) && !$this->view->file_exists($this->privateKeyPath)) { \OCP\Util::writeLog('Encryption library', - 'public key exists but private key is missing for "' . $this->userId . '"', \OCP\Util::FATAL); + 'public key exists but private key is missing for "' . $this->keyId . '"', \OCP\Util::FATAL); return false; } else { if (!$this->view->file_exists($this->publicKeyPath) && $this->view->file_exists($this->privateKeyPath) ) { \OCP\Util::writeLog('Encryption library', - 'private key exists but public key is missing for "' . $this->userId . '"', \OCP\Util::FATAL); + 'private key exists but public key is missing for "' . $this->keyId . '"', \OCP\Util::FATAL); return false; } } @@ -1046,7 +1045,7 @@ class Util { $encKeyfile = Keymanager::getFileKey($this->view, $this, $filePath); // The file has a shareKey and must use it for decryption - $shareKey = Keymanager::getShareKey($this->view, $this->userId, $this, $filePath); + $shareKey = Keymanager::getShareKey($this->view, $this->keyId, $this, $filePath); $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); @@ -1322,7 +1321,7 @@ class Util { // handle public access if ($this->isPublic) { $filename = $path; - $fileOwnerUid = $this->ownerId; + $fileOwnerUid = $this->userId; return array( $fileOwnerUid, @@ -1547,6 +1546,13 @@ class Util { return $this->userId; } + /** + * @return string + */ + public function getKeyId() { + return $this->keyId; + } + /** * @return string */ From c7dc6dc2c2d0d2de72256a6f7bcacaf3ec59bd0c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 21 Nov 2013 11:11:15 +0100 Subject: [PATCH 10/47] fix getFileKey() call --- apps/files_encryption/lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index a9468e34d4..ca9651742f 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -461,7 +461,7 @@ class Util { $relPath = Helper::stripUserFilesPath($path); } - $fileKey = Keymanager::getFileKey($this->view, $relPath); + $fileKey = Keymanager::getFileKey($this->view, $this, $relPath); if ($fileKey === false) { return false; From d2e6f7d9790d42faf9d79b7beb07b2d287fd2da3 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 21 Nov 2013 15:57:49 +0100 Subject: [PATCH 11/47] check HTTP Referer to check if we come from public.php or from a internal page. Necessary to detect public access also if a user is logged in. --- apps/files_encryption/lib/helper.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index e66a84d909..897b5d4662 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -225,7 +225,7 @@ class Helper { * @return bool */ public static function isPublicAccess() { - if (\OCP\USER::getUser() === false) { + if (strpos($_SERVER['HTTP_REFERER'], 'public.php') !== false) { return true; } else { return false; @@ -252,16 +252,22 @@ class Helper { return $relPath; } + /** + * @brief get user from the path, because we can't assume that \OCP\User::getUser() + * will always return the right result + * @param type $path + * @return boolean + */ public static function getUser($path) { $user = \OCP\User::getUser(); - // if we are logged in, than we return the userid - if ($user) { + // if we are logged in and if we don't come from a public URL, then we return the userid + if ($user && strpos($_SERVER['HTTP_REFERER'], 'public.php') === false) { return $user; } - // if no user is logged in we try to access a publically shared files. + // ...otherwise we try to access a publically shared files. // In this case we need to try to get the user from the path $trimmed = ltrim($path, '/'); From 54f0deff2ae60b2914f2cd1f20e7bcb23726edac Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 21 Nov 2013 17:02:37 +0100 Subject: [PATCH 12/47] LDAP: get user photo from LDAP and set it as avatar if available --- apps/user_ldap/lib/configuration.php | 3 ++ apps/user_ldap/user_ldap.php | 63 +++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index e14ed824a7..58f4b11e69 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -72,6 +72,7 @@ class Configuration { 'ldapExpertUsernameAttr' => null, 'ldapExpertUUIDUserAttr' => null, 'ldapExpertUUIDGroupAttr' => null, + 'lastJpegPhotoLookup' => null, ); public function __construct($configPrefix, $autoread = true) { @@ -330,6 +331,7 @@ class Configuration { 'ldap_expert_uuid_user_attr' => '', 'ldap_expert_uuid_group_attr' => '', 'has_memberof_filter_support' => 0, + 'last_jpegPhoto_lookup' => 0, ); } @@ -377,6 +379,7 @@ class Configuration { 'ldap_expert_uuid_user_attr' => 'ldapExpertUUIDUserAttr', 'ldap_expert_uuid_group_attr' => 'ldapExpertUUIDGroupAttr', 'has_memberof_filter_support' => 'hasMemberOfFilterSupport', + 'last_jpegPhoto_lookup' => 'lastJpegPhotoLookup', ); return $array; } diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 6f52bbdf23..2bff4b2821 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -69,6 +69,65 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface { } } + /** + * @brief reads jpegPhoto and set is as avatar if available + * @param $uid string ownCloud user name + * @param $dn string the user's LDAP DN + * @return void + */ + private function updateAvatar($uid, $dn) { + $lastChecked = $this->access->connection->lastJpegPhotoLookup; + if((time() - $lastChecked) < 86400 ) { + //update only once a day + return; + } + + $jpegPhoto = $this->access->readAttribute($dn, 'jpegPhoto'); + $this->access->connection->lastJpegPhotoLookup = time(); + if(!$jpegPhoto || !is_array($jpegPhoto) || !isset($jpegPhoto[0])) { + //not set, nothing left to do; + return; + } + + $image = new \OCP\Image($jpegPhoto[0]); + if(!$image->valid()) { + \OCP\Util::writeLog('user_ldap', 'jpegPhoto data invalid for '.$dn, + \OCP\Util::ERROR); + return; + } + //make sure it is a square and not bigger than 128x128 + $size = min(array($image->width(), $image->height(), 128)); + if(!$image->centerCrop($size)) { + \OCP\Util::writeLog('user_ldap', + 'croping image for avatar failed for '.$dn, + \OCP\Util::ERROR); + return; + } + + $avatarManager = \OC::$server->getAvatarManager(); + $avatar = $avatarManager->getAvatar($uid); + $avatar->set($image->data()); + } + + /** + * @brief checks whether the user is allowed to change his avatar in ownCloud + * @param $uid string the ownCloud user name + * @return boolean either the user can or cannot + */ + public function canChangeAvatar($uid) { + $dn = $this->access->username2dn($uid); + if(!$dn) { + return false; + } + $jpegPhoto = $this->access->readAttribute($dn, 'jpegPhoto'); + if(!$jpegPhoto || !is_array($jpegPhoto) || !isset($jpegPhoto[0])) { + //The user is allowed to change his avatar in ownCloud only if no + //avatar is provided by LDAP + return true; + } + return false; + } + /** * @brief Check if the password is correct * @param $uid The username @@ -173,6 +232,7 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface { $this->access->connection->writeToCache('userExists'.$uid, true); $this->updateQuota($dn); + $this->updateAvatar($uid, $dn); return true; } @@ -289,7 +349,8 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface { public function implementsActions($actions) { return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_GET_HOME - | OC_USER_BACKEND_GET_DISPLAYNAME) + | OC_USER_BACKEND_GET_DISPLAYNAME + | OC_USER_BACKEND_PROVIDE_AVATAR) & $actions); } From 8ccac86c9893fe0af1715288ce29d85091bca9aa Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 22 Nov 2013 13:24:11 +0100 Subject: [PATCH 13/47] Enable user backends to provide avatar images --- lib/private/user.php | 16 +++++++ lib/private/user/backend.php | 15 ++++--- lib/private/user/user.php | 12 ++++++ settings/personal.php | 1 + settings/templates/personal.php | 4 ++ tests/lib/user/avataruserdummy.php | 27 ++++++++++++ tests/lib/user/user.php | 69 ++++++++++++++++++++++++++++++ 7 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 tests/lib/user/avataruserdummy.php diff --git a/lib/private/user.php b/lib/private/user.php index f15fdf1dbb..d4be8eb9bd 100644 --- a/lib/private/user.php +++ b/lib/private/user.php @@ -412,6 +412,22 @@ class OC_User { } } + /** + * @brief Check whether user can change his avatar + * @param string $uid The username + * @return bool + * + * Check whether a specified user can change his avatar + */ + public static function canUserChangeAvatar($uid) { + $user = self::getManager()->get($uid); + if ($user) { + return $user->canChangeAvatar(); + } else { + return false; + } + } + /** * @brief Check whether user can change his password * @param string $uid The username diff --git a/lib/private/user/backend.php b/lib/private/user/backend.php index e9be08e429..02c93d13bd 100644 --- a/lib/private/user/backend.php +++ b/lib/private/user/backend.php @@ -31,13 +31,13 @@ define('OC_USER_BACKEND_NOT_IMPLEMENTED', -501); /** * actions that user backends can define */ -define('OC_USER_BACKEND_CREATE_USER', 0x000001); -define('OC_USER_BACKEND_SET_PASSWORD', 0x000010); -define('OC_USER_BACKEND_CHECK_PASSWORD', 0x000100); -define('OC_USER_BACKEND_GET_HOME', 0x001000); -define('OC_USER_BACKEND_GET_DISPLAYNAME', 0x010000); -define('OC_USER_BACKEND_SET_DISPLAYNAME', 0x100000); - +define('OC_USER_BACKEND_CREATE_USER', 0x0000001); +define('OC_USER_BACKEND_SET_PASSWORD', 0x0000010); +define('OC_USER_BACKEND_CHECK_PASSWORD', 0x0000100); +define('OC_USER_BACKEND_GET_HOME', 0x0001000); +define('OC_USER_BACKEND_GET_DISPLAYNAME', 0x0010000); +define('OC_USER_BACKEND_SET_DISPLAYNAME', 0x0100000); +define('OC_USER_BACKEND_PROVIDE_AVATAR', 0x1000000); /** * Abstract base class for user management. Provides methods for querying backend @@ -54,6 +54,7 @@ abstract class OC_User_Backend implements OC_User_Interface { OC_USER_BACKEND_GET_HOME => 'getHome', OC_USER_BACKEND_GET_DISPLAYNAME => 'getDisplayName', OC_USER_BACKEND_SET_DISPLAYNAME => 'setDisplayName', + OC_USER_BACKEND_PROVIDE_AVATAR => 'canChangeAvatar', ); /** diff --git a/lib/private/user/user.php b/lib/private/user/user.php index e5f842944f..e773473ec4 100644 --- a/lib/private/user/user.php +++ b/lib/private/user/user.php @@ -139,6 +139,18 @@ class User { return \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data") . '/' . $this->uid; //TODO switch to Config object once implemented } + /** + * check if the backend allows the user to change his avatar on Personal page + * + * @return bool + */ + public function canChangeAvatar() { + if($this->backend->implementsActions(\OC_USER_BACKEND_PROVIDE_AVATAR)) { + return $this->backend->canChangeAvatar($this->uid); + } + return true; + } + /** * check if the backend supports changing passwords * diff --git a/settings/personal.php b/settings/personal.php index 670e18e20e..44e1048941 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -90,6 +90,7 @@ $tmpl->assign('displayNameChangeSupported', OC_User::canUserChangeDisplayName(OC $tmpl->assign('displayName', OC_User::getDisplayName()); $tmpl->assign('enableDecryptAll' , $enableDecryptAll); $tmpl->assign('enableAvatars', \OC_Config::getValue('enable_avatars', true)); +$tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser())); $forms=OC_App::getForms('personal'); $tmpl->assign('forms', array()); diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 9d21e18e73..27a581af9c 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -87,11 +87,15 @@ if($_['passwordChangeSupported']) {

+
t('Upload new')); ?>
t('Select new from Files')); ?>
t('Remove image')); ?>

t('Either png or jpg. Ideally square but you will be able to crop it.')); ?> + + t('Your avatar is provided by your original account.')); ?> +