diff --git a/apps/files_external/lib/AppInfo/Application.php b/apps/files_external/lib/AppInfo/Application.php index 5b12377fdb..01de6f1e40 100644 --- a/apps/files_external/lib/AppInfo/Application.php +++ b/apps/files_external/lib/AppInfo/Application.php @@ -94,7 +94,8 @@ class Application extends App implements IBackendProvider, IAuthMechanismProvide // TODO: obsolete these and use the TokenProvider to get the user's password from the session $this->getAuthMechanisms(); - // app developers: do NOT depend on this! it will disappear with oC 9.0! + // don't remove this, as app loading order might be a side effect and + // querying the service from the server not reliable \OC::$server->getEventDispatcher()->dispatch( 'OCA\\Files_External::loadAdditionalBackends' ); diff --git a/apps/files_external/lib/Lib/Storage/FTP.php b/apps/files_external/lib/Lib/Storage/FTP.php index dc4ab9cb0e..db2ae9cf29 100644 --- a/apps/files_external/lib/Lib/Storage/FTP.php +++ b/apps/files_external/lib/Lib/Storage/FTP.php @@ -44,8 +44,6 @@ class FTP extends StreamWrapper{ private $secure; private $root; - private static $tempFiles=array(); - public function __construct($params) { if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { $this->host=$params['host']; diff --git a/apps/files_external/lib/Service/BackendService.php b/apps/files_external/lib/Service/BackendService.php index 05bda1998d..3c66e8eb3c 100644 --- a/apps/files_external/lib/Service/BackendService.php +++ b/apps/files_external/lib/Service/BackendService.php @@ -328,7 +328,7 @@ class BackendService { 'Handler for %s is not an instance of IConfigHandler', $placeholder )); } - $this->configHandlers[] = $handler; + $this->configHandlers[$placeholder] = $handler; $newLoaded = true; } $this->configHandlerLoaders = []; diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index f371ef9efb..5afd928301 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -34,6 +34,8 @@ return new OCA\User_LDAP\GroupPluginManager(); }); +$app = new \OCA\User_LDAP\AppInfo\Application(); + $helper = new \OCA\User_LDAP\Helper(\OC::$server->getConfig()); $configPrefixes = $helper->getServerConfigurationPrefixes(true); if(count($configPrefixes) > 0) { @@ -67,6 +69,8 @@ if(count($configPrefixes) > 0) { OC::$server->getEventDispatcher()->dispatch('OCA\\User_LDAP\\User\\User::postLDAPBackendAdded'); \OC::$server->getGroupManager()->addBackend($groupBackend); + + $app->registerBackendDependents(); } \OCP\Util::connectHook( diff --git a/apps/user_ldap/composer/composer/autoload_classmap.php b/apps/user_ldap/composer/composer/autoload_classmap.php index e25b7ee312..fadbc701ec 100644 --- a/apps/user_ldap/composer/composer/autoload_classmap.php +++ b/apps/user_ldap/composer/composer/autoload_classmap.php @@ -23,12 +23,14 @@ return array( 'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php', 'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php', 'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php', + 'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => $baseDir . '/../lib/Exceptions/AttributeNotSet.php', 'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir . '/../lib/Exceptions/ConstraintViolationException.php', 'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => $baseDir . '/../lib/Exceptions/NotOnLDAP.php', 'OCA\\User_LDAP\\FilesystemHelper' => $baseDir . '/../lib/FilesystemHelper.php', 'OCA\\User_LDAP\\GroupPluginManager' => $baseDir . '/../lib/GroupPluginManager.php', 'OCA\\User_LDAP\\Group_LDAP' => $baseDir . '/../lib/Group_LDAP.php', 'OCA\\User_LDAP\\Group_Proxy' => $baseDir . '/../lib/Group_Proxy.php', + 'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => $baseDir . '/../lib/Handler/ExtStorageConfigHandler.php', 'OCA\\User_LDAP\\Helper' => $baseDir . '/../lib/Helper.php', 'OCA\\User_LDAP\\IGroupLDAP' => $baseDir . '/../lib/IGroupLDAP.php', 'OCA\\User_LDAP\\ILDAPGroupPlugin' => $baseDir . '/../lib/ILDAPGroupPlugin.php', diff --git a/apps/user_ldap/composer/composer/autoload_static.php b/apps/user_ldap/composer/composer/autoload_static.php index 23819055be..d40df6e483 100644 --- a/apps/user_ldap/composer/composer/autoload_static.php +++ b/apps/user_ldap/composer/composer/autoload_static.php @@ -38,12 +38,14 @@ class ComposerStaticInitUser_LDAP 'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php', 'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php', 'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php', + 'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => __DIR__ . '/..' . '/../lib/Exceptions/AttributeNotSet.php', 'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__ . '/..' . '/../lib/Exceptions/ConstraintViolationException.php', 'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => __DIR__ . '/..' . '/../lib/Exceptions/NotOnLDAP.php', 'OCA\\User_LDAP\\FilesystemHelper' => __DIR__ . '/..' . '/../lib/FilesystemHelper.php', 'OCA\\User_LDAP\\GroupPluginManager' => __DIR__ . '/..' . '/../lib/GroupPluginManager.php', 'OCA\\User_LDAP\\Group_LDAP' => __DIR__ . '/..' . '/../lib/Group_LDAP.php', 'OCA\\User_LDAP\\Group_Proxy' => __DIR__ . '/..' . '/../lib/Group_Proxy.php', + 'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => __DIR__ . '/..' . '/../lib/Handler/ExtStorageConfigHandler.php', 'OCA\\User_LDAP\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php', 'OCA\\User_LDAP\\IGroupLDAP' => __DIR__ . '/..' . '/../lib/IGroupLDAP.php', 'OCA\\User_LDAP\\ILDAPGroupPlugin' => __DIR__ . '/..' . '/../lib/ILDAPGroupPlugin.php', diff --git a/apps/user_ldap/lib/AppInfo/Application.php b/apps/user_ldap/lib/AppInfo/Application.php index 59d7cdb492..7e0c353448 100644 --- a/apps/user_ldap/lib/AppInfo/Application.php +++ b/apps/user_ldap/lib/AppInfo/Application.php @@ -23,7 +23,9 @@ namespace OCA\User_LDAP\AppInfo; +use OCA\Files_External\Service\BackendService; use OCA\User_LDAP\Controller\RenewPasswordController; +use OCA\User_LDAP\Handler\ExtStorageConfigHandler; use OCA\User_LDAP\ILDAPWrapper; use OCA\User_LDAP\LDAP; use OCP\AppFramework\App; @@ -57,4 +59,18 @@ class Application extends App { return new LDAP(); }); } + + public function registerBackendDependents() { + $container = $this->getContainer(); + + $container->getServer()->getEventDispatcher()->addListener( + 'OCA\\Files_External::loadAdditionalBackends', + function() use ($container) { + $storagesBackendService = $container->query(BackendService::class); + $storagesBackendService->registerConfigHandler('home', function () use ($container) { + return $container->query(ExtStorageConfigHandler::class); + }); + } + ); + } } diff --git a/apps/user_ldap/lib/Configuration.php b/apps/user_ldap/lib/Configuration.php index c912d30b49..ee77702a09 100644 --- a/apps/user_ldap/lib/Configuration.php +++ b/apps/user_ldap/lib/Configuration.php @@ -106,6 +106,7 @@ class Configuration { 'turnOnPasswordChange' => false, 'ldapDynamicGroupMemberURL' => null, 'ldapDefaultPPolicyDN' => null, + 'ldapExtStorageHomeAttribute' => null, ); /** @@ -477,6 +478,7 @@ class Configuration { 'ldap_dynamic_group_member_url' => '', 'ldap_default_ppolicy_dn' => '', 'ldap_user_avatar_rule' => 'default', + 'ldap_ext_storage_home_attribute' => '', ); } @@ -537,6 +539,7 @@ class Configuration { 'ldap_experienced_admin' => 'ldapExperiencedAdmin', 'ldap_dynamic_group_member_url' => 'ldapDynamicGroupMemberURL', 'ldap_default_ppolicy_dn' => 'ldapDefaultPPolicyDN', + 'ldap_ext_storage_home_attribute' => 'ldapExtStorageHomeAttribute', 'ldapIgnoreNamingRules' => 'ldapIgnoreNamingRules', // sysconfig ); return $array; diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php index 7becf311a2..ba393dffc1 100644 --- a/apps/user_ldap/lib/Connection.php +++ b/apps/user_ldap/lib/Connection.php @@ -60,6 +60,8 @@ use OCP\ILogger; * @property string ldapQuotaAttribute * @property string ldapQuotaDefault * @property string ldapEmailAttribute + * @property string ldapExtStorageHomeAttribute + * @property string homeFolderNamingRule */ class Connection extends LDAPUtility { private $ldapConnectionRes = null; diff --git a/apps/user_ldap/lib/Exceptions/AttributeNotSet.php b/apps/user_ldap/lib/Exceptions/AttributeNotSet.php new file mode 100644 index 0000000000..540b65c182 --- /dev/null +++ b/apps/user_ldap/lib/Exceptions/AttributeNotSet.php @@ -0,0 +1,26 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\User_LDAP\Exceptions; + +class AttributeNotSet extends \RuntimeException {} diff --git a/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php b/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php new file mode 100644 index 0000000000..98a3cc7126 --- /dev/null +++ b/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php @@ -0,0 +1,74 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\User_LDAP\Handler; + +use OCA\Files_External\Config\IConfigHandler; +use OCA\Files_External\Config\SimpleSubstitutionTrait; +use OCA\User_LDAP\User_Proxy; +use OCP\IUserSession; + +class ExtStorageConfigHandler implements IConfigHandler { + use SimpleSubstitutionTrait; + + /** @var IUserSession */ + private $session; + + public function __construct(IUserSession $session) { + $this->placeholder = 'home'; + $this->session = $session; + } + + /** + * @param mixed $optionValue + * @return mixed the same type as $optionValue + * @since 16.0.0 + * @throws \Exception + */ + public function handle($optionValue) { + $user = $this->session->getUser(); + if($user === null) { + return $optionValue; + } + + $backend = $user->getBackend(); + if(!$backend instanceof User_Proxy) { + return $optionValue; + } + + $access = $backend->getLDAPAccess($user->getUID()); + if(!$access) { + return $optionValue; + } + + $attribute = $access->connection->ldapExtStorageHomeAttribute; + if(empty($attribute)) { + return $optionValue; + } + + $ldapUser = $access->userManager->get($user->getUID()); + $extHome = $ldapUser->getExtStorageHome(); + + return $this->processInput($optionValue, $extHome); + } +} diff --git a/apps/user_ldap/lib/User/Manager.php b/apps/user_ldap/lib/User/Manager.php index 6185c0da45..046b42551b 100644 --- a/apps/user_ldap/lib/User/Manager.php +++ b/apps/user_ldap/lib/User/Manager.php @@ -176,6 +176,7 @@ class Manager { $this->access->getConnection()->ldapEmailAttribute, $this->access->getConnection()->ldapUserDisplayName, $this->access->getConnection()->ldapUserDisplayName2, + $this->access->getConnection()->ldapExtStorageHomeAttribute, ]; $homeRule = $this->access->getConnection()->homeFolderNamingRule; diff --git a/apps/user_ldap/lib/User/User.php b/apps/user_ldap/lib/User/User.php index 0d8f993746..d68d8b35d1 100644 --- a/apps/user_ldap/lib/User/User.php +++ b/apps/user_ldap/lib/User/User.php @@ -32,6 +32,7 @@ namespace OCA\User_LDAP\User; use OCA\User_LDAP\Access; use OCA\User_LDAP\Connection; +use OCA\User_LDAP\Exceptions\AttributeNotSet; use OCA\User_LDAP\FilesystemHelper; use OCA\User_LDAP\LogWrapper; use OCP\IAvatarManager; @@ -244,6 +245,13 @@ class User { } $this->connection->writeToCache($cacheKey, $groups); + //external storage var + $attr = strtolower($this->connection->ldapExtStorageHomeAttribute); + if(isset($ldapEntry[$attr])) { + $this->updateExtStorageHome($ldapEntry[$attr][0]); + } + unset($attr); + //Avatar /** @var Connection $connection */ $connection = $this->access->getConnection(); @@ -616,6 +624,47 @@ class User { return false; } + /** + * @throws AttributeNotSet + * @throws \OC\ServerNotAvailableException + * @throws \OCP\PreConditionNotMetException + */ + public function getExtStorageHome():string { + $value = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', ''); + if ($value !== '') { + return $value; + } + + $value = $this->updateExtStorageHome(); + if ($value !== '') { + return $value; + } + + throw new AttributeNotSet(sprintf( + 'external home storage attribute yield no value for %s', $this->getUsername() + )); + } + + /** + * @throws \OCP\PreConditionNotMetException + * @throws \OC\ServerNotAvailableException + */ + public function updateExtStorageHome(string $valueFromLDAP = null):string { + if($valueFromLDAP === null) { + $extHomeValues = $this->access->readAttribute($this->getDN(), $this->connection->ldapExtStorageHomeAttribute); + } else { + $extHomeValues = [$valueFromLDAP]; + } + if ($extHomeValues && isset($extHomeValues[0])) { + $extHome = $extHomeValues[0]; + $this->config->setUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', $extHome); + return $extHome; + } else { + $this->config->deleteUserValue($this->getUsername(), 'user_ldap', 'extStorageHome'); + return ''; + } + } + /** * called by a post_login hook to handle password expiry * diff --git a/apps/user_ldap/tests/ConfigurationTest.php b/apps/user_ldap/tests/ConfigurationTest.php index ab1312860f..6e45f16386 100644 --- a/apps/user_ldap/tests/ConfigurationTest.php +++ b/apps/user_ldap/tests/ConfigurationTest.php @@ -97,6 +97,8 @@ class ConfigurationTest extends \Test\TestCase { 'set avatar rule, default' => ['ldapUserAvatarRule', 'default', 'default'], 'set avatar rule, none' => ['ldapUserAvatarRule', 'none', 'none'], 'set avatar rule, data attribute' => ['ldapUserAvatarRule', 'data:jpegPhoto', 'data:jpegPhoto'], + + 'set external storage home attribute' => ['ldapExtStorageHomeAttribute', 'homePath', 'homePath'], ); } diff --git a/apps/user_ldap/tests/User/UserTest.php b/apps/user_ldap/tests/User/UserTest.php index 6ff9defe47..f99100789d 100644 --- a/apps/user_ldap/tests/User/UserTest.php +++ b/apps/user_ldap/tests/User/UserTest.php @@ -789,6 +789,50 @@ class UserTest extends \Test\TestCase { $this->user->update(); } + public function extStorageHomeDataProvider() { + return [ + [ 'myFolder', null ], + [ '', null, false ], + [ 'myFolder', 'myFolder' ], + ]; + } + + /** + * @dataProvider extStorageHomeDataProvider + */ + public function testUpdateExtStorageHome(string $expected, string $valueFromLDAP = null, bool $isSet = true) { + if($valueFromLDAP === null) { + $this->connection->expects($this->once()) + ->method('__get') + ->willReturnMap([ + ['ldapExtStorageHomeAttribute', 'homeDirectory'], + ]); + + $return = []; + if($isSet) { + $return[] = $expected; + } + $this->access->expects($this->once()) + ->method('readAttribute') + ->with($this->dn, 'homeDirectory') + ->willReturn($return); + } + + if($expected !== '') { + $this->config->expects($this->once()) + ->method('setUserValue') + ->with($this->uid, 'user_ldap', 'extStorageHome', $expected); + } else { + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with($this->uid, 'user_ldap', 'extStorageHome'); + } + + $actual = $this->user->updateExtStorageHome($valueFromLDAP); + $this->assertSame($expected, $actual); + + } + public function testUpdateNoRefresh() { $this->config->expects($this->at(0)) ->method('getUserValue') @@ -867,15 +911,16 @@ class UserTest extends \Test\TestCase { } public function testProcessAttributes() { - $requiredMethods = array( + $requiredMethods = [ 'markRefreshTime', 'updateQuota', 'updateEmail', 'composeAndStoreDisplayName', 'storeLDAPUserName', 'getHomePath', - 'updateAvatar' - ); + 'updateAvatar', + 'updateExtStorageHome', + ]; /** @var User|\PHPUnit_Framework_MockObject_MockObject $userMock */ $userMock = $this->getMockBuilder(User::class) @@ -914,6 +959,7 @@ class UserTest extends \Test\TestCase { strtolower($this->connection->ldapQuotaAttribute) => ['4096'], strtolower($this->connection->ldapEmailAttribute) => ['alice@wonderland.org'], strtolower($this->connection->ldapUserDisplayName) => ['Aaaaalice'], + strtolower($this->connection->ldapExtStorageHomeAttribute) => ['homeDirectory'], 'uid' => [$this->uid], 'homedirectory' => ['Alice\'s Folder'], 'memberof' => ['cn=groupOne', 'cn=groupTwo'],