* handler registered upon OCA\\Files_External::loadAdditionalBackends event as user_ldap is loaded before files_external * new configuration field "ldapExtStorageHomeAttribute" (not in GUI yet) Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>tags/v16.0.0alpha1
@@ -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' | |||
); |
@@ -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']; |
@@ -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 = []; |
@@ -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( |
@@ -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', |
@@ -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', |
@@ -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); | |||
}); | |||
} | |||
); | |||
} | |||
} |
@@ -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; |
@@ -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; |
@@ -0,0 +1,26 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> | |||
* | |||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de> | |||
* | |||
* @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 <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCA\User_LDAP\Exceptions; | |||
class AttributeNotSet extends \RuntimeException {} |
@@ -0,0 +1,74 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> | |||
* | |||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de> | |||
* | |||
* @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 <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
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); | |||
} | |||
} |
@@ -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; |
@@ -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 | |||
* |
@@ -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'], | |||
); | |||
} | |||
@@ -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'], |