summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblizzz <blizzz@arthur-schiwon.de>2019-02-15 23:09:45 +0100
committerGitHub <noreply@github.com>2019-02-15 23:09:45 +0100
commitbfd61d849f1f8dc726b1dc42d827bcb98bfe8b9d (patch)
treef08973b8a0c420c3479d9a2574ffc10aa6e077c8
parentbf19431f2a1c08e62d78506a344c99dc4ea4078b (diff)
parent173836b95af7cdc87cb603c5d98494b02242a949 (diff)
downloadnextcloud-server-bfd61d849f1f8dc726b1dc42d827bcb98bfe8b9d.tar.gz
nextcloud-server-bfd61d849f1f8dc726b1dc42d827bcb98bfe8b9d.zip
Merge pull request #14174 from nextcloud/feature/noid/extstorage-mountconfighandler
Mount configuration handlers for external storages
-rw-r--r--apps/files_external/lib/AppInfo/Application.php15
-rw-r--r--apps/files_external/lib/Config/ConfigAdapter.php6
-rw-r--r--apps/files_external/lib/Config/IConfigHandler.php39
-rw-r--r--apps/files_external/lib/Config/SimpleSubstitutionTrait.php86
-rw-r--r--apps/files_external/lib/Config/UserPlaceholderHandler.php53
-rw-r--r--apps/files_external/lib/Lib/Storage/FTP.php2
-rw-r--r--apps/files_external/lib/Service/BackendService.php69
-rw-r--r--apps/files_external/lib/config.php67
-rw-r--r--apps/files_external/tests/Config/UserPlaceholderHandlerTest.php81
-rw-r--r--apps/files_external/tests/Service/BackendServiceTest.php76
-rw-r--r--apps/user_ldap/appinfo/app.php4
-rw-r--r--apps/user_ldap/composer/composer/autoload_classmap.php2
-rw-r--r--apps/user_ldap/composer/composer/autoload_static.php2
-rw-r--r--apps/user_ldap/js/wizard/wizardTabAdvanced.js15
-rw-r--r--apps/user_ldap/lib/AppInfo/Application.php16
-rw-r--r--apps/user_ldap/lib/Configuration.php3
-rw-r--r--apps/user_ldap/lib/Connection.php2
-rw-r--r--apps/user_ldap/lib/Exceptions/AttributeNotSet.php26
-rw-r--r--apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php74
-rw-r--r--apps/user_ldap/lib/User/Manager.php1
-rw-r--r--apps/user_ldap/lib/User/User.php49
-rw-r--r--apps/user_ldap/templates/settings.php1
-rw-r--r--apps/user_ldap/tests/ConfigurationTest.php2
-rw-r--r--apps/user_ldap/tests/User/UserTest.php52
24 files changed, 703 insertions, 40 deletions
diff --git a/apps/files_external/lib/AppInfo/Application.php b/apps/files_external/lib/AppInfo/Application.php
index 8eebc550d09..01de6f1e40b 100644
--- a/apps/files_external/lib/AppInfo/Application.php
+++ b/apps/files_external/lib/AppInfo/Application.php
@@ -29,6 +29,7 @@
namespace OCA\Files_External\AppInfo;
+use OCA\Files_External\Config\UserPlaceholderHandler;
use OCA\Files_External\Lib\Auth\PublicKey\RSAPrivateKey;
use OCA\Files_External\Lib\Auth\SMB\KerberosAuth;
use \OCP\AppFramework\App;
@@ -67,7 +68,12 @@ use OCP\Files\Config\IUserMountCache;
*/
class Application extends App implements IBackendProvider, IAuthMechanismProvider {
- public function __construct(array $urlParams = array()) {
+ /**
+ * Application constructor.
+ *
+ * @throws \OCP\AppFramework\QueryException
+ */
+ public function __construct(array $urlParams = []) {
parent::__construct('files_external', $urlParams);
$container = $this->getContainer();
@@ -76,15 +82,20 @@ class Application extends App implements IBackendProvider, IAuthMechanismProvide
return $c->getServer()->query('UserMountCache');
});
+ /** @var BackendService $backendService */
$backendService = $container->query(BackendService::class);
$backendService->registerBackendProvider($this);
$backendService->registerAuthMechanismProvider($this);
+ $backendService->registerConfigHandler('user', function() use ($container) {
+ return $container->query(UserPlaceholderHandler::class);
+ });
// force-load auth mechanisms since some will register hooks
// 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/Config/ConfigAdapter.php b/apps/files_external/lib/Config/ConfigAdapter.php
index 34e96df0441..8d9c7ea8d1b 100644
--- a/apps/files_external/lib/Config/ConfigAdapter.php
+++ b/apps/files_external/lib/Config/ConfigAdapter.php
@@ -29,7 +29,6 @@ namespace OCA\Files_External\Config;
use OC\Files\Storage\Wrapper\Availability;
use OCA\Files_External\Migration\StorageMigrator;
use OCP\Files\Storage;
-use OC\Files\Mount\MountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCA\Files_External\Lib\PersonalMount;
use OCP\Files\Config\IMountProvider;
@@ -73,12 +72,11 @@ class ConfigAdapter implements IMountProvider {
*
* @param StorageConfig $storage
* @param IUser $user
+ * @throws \OCP\AppFramework\QueryException
*/
private function prepareStorageConfig(StorageConfig &$storage, IUser $user) {
foreach ($storage->getBackendOptions() as $option => $value) {
- $storage->setBackendOption($option, \OC_Mount_Config::setUserVars(
- $user->getUID(), $value
- ));
+ $storage->setBackendOption($option, \OC_Mount_Config::substitutePlaceholdersInConfig($value));
}
$objectStore = $storage->getBackendOption('objectstore');
diff --git a/apps/files_external/lib/Config/IConfigHandler.php b/apps/files_external/lib/Config/IConfigHandler.php
new file mode 100644
index 00000000000..3e2ec32ba23
--- /dev/null
+++ b/apps/files_external/lib/Config/IConfigHandler.php
@@ -0,0 +1,39 @@
+<?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\Files_External\Config;
+
+/**
+ * Interface IConfigHandler
+ *
+ * @package OCA\Files_External\Config
+ * @since 16.0.0
+ */
+interface IConfigHandler {
+ /**
+ * @param mixed $optionValue
+ * @return mixed the same type as $optionValue
+ * @since 16.0.0
+ */
+ public function handle($optionValue);
+}
diff --git a/apps/files_external/lib/Config/SimpleSubstitutionTrait.php b/apps/files_external/lib/Config/SimpleSubstitutionTrait.php
new file mode 100644
index 00000000000..05bd529f1c1
--- /dev/null
+++ b/apps/files_external/lib/Config/SimpleSubstitutionTrait.php
@@ -0,0 +1,86 @@
+<?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\Files_External\Config;
+
+/**
+ * Trait SimpleSubstitutionTrait
+ *
+ * @package OCA\Files_External\Config
+ * @since 16.0.0
+ */
+trait SimpleSubstitutionTrait {
+ /**
+ * @var string the placeholder without $ prefix
+ * @since 16.0.0
+ */
+ private $placeholder;
+
+ /** @var string */
+ protected $sanitizedPlaceholder;
+
+ /**
+ * @param mixed $optionValue
+ * @param string $replacement
+ * @return mixed
+ * @since 16.0.0
+ */
+ private function processInput($optionValue, string $replacement) {
+ $this->checkPlaceholder();
+ if (is_array($optionValue)) {
+ foreach ($optionValue as &$value) {
+ $value = $this->substituteIfString($value, $replacement);
+ }
+ } else {
+ $optionValue = $this->substituteIfString($optionValue, $replacement);
+ }
+ return $optionValue;
+ }
+
+ /**
+ * @throws \RuntimeException
+ */
+ protected function checkPlaceholder(): void {
+ $this->sanitizedPlaceholder = trim(strtolower($this->placeholder));
+ if(!(bool)\preg_match('/^[a-z0-9]*$/', $this->sanitizedPlaceholder)) {
+ throw new \RuntimeException(sprintf(
+ 'Invalid placeholder %s, only [a-z0-9] are allowed', $this->sanitizedPlaceholder
+ ));
+ }
+ if($this->sanitizedPlaceholder === '') {
+ throw new \RuntimeException('Invalid empty placeholder');
+ }
+ }
+
+ /**
+ * @param mixed $value
+ * @param string $replacement
+ * @return mixed
+ */
+ protected function substituteIfString($value, string $replacement) {
+ if(is_string($value)) {
+ return str_ireplace('$' . $this->sanitizedPlaceholder, $replacement, $value);
+ }
+ return $value;
+ }
+}
diff --git a/apps/files_external/lib/Config/UserPlaceholderHandler.php b/apps/files_external/lib/Config/UserPlaceholderHandler.php
new file mode 100644
index 00000000000..721d3bbe02a
--- /dev/null
+++ b/apps/files_external/lib/Config/UserPlaceholderHandler.php
@@ -0,0 +1,53 @@
+<?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\Files_External\Config;
+
+use OCP\IUserSession;
+
+class UserPlaceholderHandler implements IConfigHandler {
+ use SimpleSubstitutionTrait;
+
+ /** @var IUserSession */
+ private $session;
+
+ public function __construct(IUserSession $session) {
+ $this->session = $session;
+ $this->placeholder = 'user';
+ }
+
+ /**
+ * @param mixed $optionValue
+ * @return mixed the same type as $optionValue
+ * @since 16.0.0
+ */
+ public function handle($optionValue) {
+ $user = $this->session->getUser();
+ if($user === null) {
+ return $optionValue;
+ }
+ $uid = $user->getUID();
+
+ return $this->processInput($optionValue, $uid);
+ }
+}
diff --git a/apps/files_external/lib/Lib/Storage/FTP.php b/apps/files_external/lib/Lib/Storage/FTP.php
index dc4ab9cb0e1..db2ae9cf298 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 bd6c525f319..bd4d2bffa7b 100644
--- a/apps/files_external/lib/Service/BackendService.php
+++ b/apps/files_external/lib/Service/BackendService.php
@@ -4,6 +4,7 @@
*
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license AGPL-3.0
*
@@ -23,6 +24,7 @@
namespace OCA\Files_External\Service;
+use OCA\Files_External\Config\IConfigHandler;
use \OCP\IConfig;
use \OCA\Files_External\Lib\Backend\Backend;
@@ -67,6 +69,11 @@ class BackendService {
/** @var IAuthMechanismProvider[] */
private $authMechanismProviders = [];
+ /** @var callable[] */
+ private $configHandlerLoaders = [];
+
+ private $configHandlers = [];
+
/**
* @param IConfig $config
*/
@@ -280,4 +287,66 @@ class BackendService {
protected function isAllowedAuthMechanism(AuthMechanism $authMechanism) {
return true; // not implemented
}
+
+ /**
+ * registers a configuration handler
+ *
+ * The function of the provided $placeholder is mostly to act a sorting
+ * criteria, so longer placeholders are replaced first. This avoids
+ * "$user" overwriting parts of "$userMail" and "$userLang", for example.
+ * The provided value should not contain the $ prefix, only a-z0-9 are
+ * allowed. Upper case letters are lower cased, the replacement is case-
+ * insensitive.
+ *
+ * The configHandlerLoader should just instantiate the handler on demand.
+ * For now all handlers are instantiated when a mount is loaded, independent
+ * of whether the placeholder is present or not. This may change in future.
+ *
+ * @since 16.0.0
+ */
+ public function registerConfigHandler(string $placeholder, callable $configHandlerLoader) {
+ $placeholder = trim(strtolower($placeholder));
+ if(!(bool)\preg_match('/^[a-z0-9]*$/', $placeholder)) {
+ throw new \RuntimeException(sprintf(
+ 'Invalid placeholder %s, only [a-z0-9] are allowed', $placeholder
+ ));
+ }
+ if($placeholder === '') {
+ throw new \RuntimeException('Invalid empty placeholder');
+ }
+ if(isset($this->configHandlerLoaders[$placeholder]) || isset($this->configHandlers[$placeholder])) {
+ throw new \RuntimeException(sprintf('A handler is already registered for %s', $placeholder));
+ }
+ $this->configHandlerLoaders[$placeholder] = $configHandlerLoader;
+ }
+
+ protected function loadConfigHandlers():void {
+ $newLoaded = false;
+ foreach ($this->configHandlerLoaders as $placeholder => $loader) {
+ $handler = $loader();
+ if(!$handler instanceof IConfigHandler) {
+ throw new \RuntimeException(sprintf(
+ 'Handler for %s is not an instance of IConfigHandler', $placeholder
+ ));
+ }
+ $this->configHandlers[$placeholder] = $handler;
+ $newLoaded = true;
+ }
+ $this->configHandlerLoaders = [];
+ if($newLoaded) {
+ // ensure those with longest placeholders come first,
+ // to avoid substring matches
+ uksort($this->configHandlers, function ($phA, $phB) {
+ return strlen($phB) <=> strlen($phA);
+ });
+ }
+ }
+
+ /**
+ * @since 16.0.0
+ */
+ public function getConfigHandlers() {
+ $this->loadConfigHandlers();
+ return $this->configHandlers;
+ }
}
diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php
index 7fb118c2de5..65e8ae387bd 100644
--- a/apps/files_external/lib/config.php
+++ b/apps/files_external/lib/config.php
@@ -3,6 +3,7 @@
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Andreas Fischer <bantu@owncloud.com>
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
* @author Björn Schießle <bjoern@schiessle.org>
* @author Frank Karlitschek <frank@karlitschek.de>
@@ -35,6 +36,8 @@
*
*/
+use OCA\Files_External\Config\IConfigHandler;
+use OCA\Files_External\Config\UserPlaceholderHandler;
use phpseclib\Crypt\AES;
use \OCA\Files_External\AppInfo\Application;
use \OCA\Files_External\Lib\Backend\LegacyBackend;
@@ -104,7 +107,7 @@ class OC_Mount_Config {
$mountPoint = '/'.$uid.'/files'.$storage->getMountPoint();
$mountEntry = self::prepareMountPointEntry($storage, false);
foreach ($mountEntry['options'] as &$option) {
- $option = self::setUserVars($uid, $option);
+ $option = self::substitutePlaceholdersInConfig($option);
}
$mountPoints[$mountPoint] = $mountEntry;
}
@@ -113,7 +116,7 @@ class OC_Mount_Config {
$mountPoint = '/'.$uid.'/files'.$storage->getMountPoint();
$mountEntry = self::prepareMountPointEntry($storage, true);
foreach ($mountEntry['options'] as &$option) {
- $option = self::setUserVars($uid, $option);
+ $option = self::substitutePlaceholdersInConfig($uid, $option);
}
$mountPoints[$mountPoint] = $mountEntry;
}
@@ -199,18 +202,26 @@ class OC_Mount_Config {
* @param string $user user value
* @param string|array $input
* @return string
+ * @deprecated use self::substitutePlaceholdersInConfig($input)
*/
public static function setUserVars($user, $input) {
- if (is_array($input)) {
- foreach ($input as &$value) {
- if (is_string($value)) {
- $value = str_replace('$user', $user, $value);
- }
- }
- } else {
- if (is_string($input)) {
- $input = str_replace('$user', $user, $input);
- }
+ $handler = self::$app->getContainer()->query(UserPlaceholderHandler::class);
+ return $handler->handle($input);
+ }
+
+ /**
+ * @param mixed $input
+ * @return mixed
+ * @throws \OCP\AppFramework\QueryException
+ * @since 16.0.0
+ */
+ public static function substitutePlaceholdersInConfig($input) {
+ /** @var BackendService $backendService */
+ $backendService = self::$app->getContainer()->query(BackendService::class);
+ /** @var IConfigHandler[] $handlers */
+ $handlers = $backendService->getConfigHandlers();
+ foreach ($handlers as $handler) {
+ $input = $handler->handle($input);
}
return $input;
}
@@ -229,7 +240,21 @@ class OC_Mount_Config {
return StorageNotAvailableException::STATUS_SUCCESS;
}
foreach ($options as &$option) {
- $option = self::setUserVars(OCP\User::getUser(), $option);
+ $option = self::substitutePlaceholdersInConfig($option);
+ if(!self::arePlaceholdersSubstituted($option)) {
+ \OC::$server->getLogger()->error(
+ 'A placeholder was not substituted: {option} for mount type {class}',
+ [
+ 'app' => 'files_external',
+ 'option' => $option,
+ 'class' => $class,
+ ]
+ );
+ throw new StorageNotAvailableException(
+ 'Mount configuration incomplete',
+ StorageNotAvailableException::STATUS_INCOMPLETE_CONF
+ );
+ }
}
if (class_exists($class)) {
try {
@@ -254,6 +279,22 @@ class OC_Mount_Config {
return StorageNotAvailableException::STATUS_ERROR;
}
+ public static function arePlaceholdersSubstituted($option):bool {
+ $result = true;
+ if(is_array($option)) {
+ foreach ($option as $optionItem) {
+ if(is_array($optionItem)) {
+ $result = $result && self::arePlaceholdersSubstituted($option);
+ }
+ }
+ } else if (is_string($option)) {
+ if (strpos($option, '$') !== false) {
+ $result = false;
+ }
+ }
+ return $result;
+ }
+
/**
* Read the mount points in the config file into an array
*
diff --git a/apps/files_external/tests/Config/UserPlaceholderHandlerTest.php b/apps/files_external/tests/Config/UserPlaceholderHandlerTest.php
new file mode 100644
index 00000000000..b6103c20b46
--- /dev/null
+++ b/apps/files_external/tests/Config/UserPlaceholderHandlerTest.php
@@ -0,0 +1,81 @@
+<?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\files_external\tests\Config;
+
+use OCA\Files_External\Config\UserPlaceholderHandler;
+use OCP\IUser;
+use OCP\IUserSession;
+
+class UserPlaceholderHandlerTest extends \Test\TestCase {
+ /** @var IUser|\PHPUnit_Framework_MockObject_MockObject */
+ protected $user;
+
+ /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
+ protected $session;
+
+ /** @var UserPlaceholderHandler */
+ protected $handler;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->user = $this->createMock(IUser::class);
+ $this->user->expects($this->any())
+ ->method('getUid')
+ ->willReturn('alice');
+ $this->session = $this->createMock(IUserSession::class);
+
+ $this->handler = new UserPlaceholderHandler($this->session);
+ }
+
+ protected function setUser() {
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($this->user);
+ }
+
+ public function optionProvider() {
+ return [
+ ['/foo/bar/$user/foobar', '/foo/bar/alice/foobar'],
+ [['/foo/bar/$user/foobar'], ['/foo/bar/alice/foobar']],
+ [['/FOO/BAR/$USER/FOOBAR'], ['/FOO/BAR/alice/FOOBAR']],
+ ];
+ }
+
+ /**
+ * @dataProvider optionProvider
+ */
+ public function testHandle($option, $expected) {
+ $this->setUser();
+ $this->assertSame($expected, $this->handler->handle($option));
+ }
+
+ /**
+ * @dataProvider optionProvider
+ */
+ public function testHandleNoUser($option) {
+ $this->assertSame($option, $this->handler->handle($option));
+ }
+
+}
diff --git a/apps/files_external/tests/Service/BackendServiceTest.php b/apps/files_external/tests/Service/BackendServiceTest.php
index e8a3181658c..71990553e8d 100644
--- a/apps/files_external/tests/Service/BackendServiceTest.php
+++ b/apps/files_external/tests/Service/BackendServiceTest.php
@@ -23,31 +23,27 @@
*/
namespace OCA\Files_External\Tests\Service;
+use OCA\Files_External\Config\IConfigHandler;
use OCA\Files_External\Lib\Auth\AuthMechanism;
use OCA\Files_External\Lib\Backend\Backend;
use OCA\Files_External\Lib\Config\IAuthMechanismProvider;
use OCA\Files_External\Lib\Config\IBackendProvider;
-use \OCA\Files_External\Service\BackendService;
+use OCA\Files_External\Service\BackendService;
use OCP\IConfig;
-use OCP\IL10N;
class BackendServiceTest extends \Test\TestCase {
- /** @var \OCP\IConfig */
+ /** @var \OCP\IConfig|\PHPUnit_Framework_MockObject_MockObject */
protected $config;
- /** @var \OCP\IL10N */
- protected $l10n;
-
protected function setUp() {
$this->config = $this->createMock(IConfig::class);
- $this->l10n = $this->createMock(IL10N::class);
}
/**
* @param string $class
*
- * @return \OCA\Files_External\Lib\Backend\Backend
+ * @return \OCA\Files_External\Lib\Backend\Backend|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getBackendMock($class) {
$backend = $this->getMockBuilder(Backend::class)
@@ -61,7 +57,7 @@ class BackendServiceTest extends \Test\TestCase {
/**
* @param string $class
*
- * @return \OCA\Files_External\Lib\Auth\AuthMechanism
+ * @return \OCA\Files_External\Lib\Auth\AuthMechanism|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getAuthMechanismMock($class) {
$backend = $this->getMockBuilder(AuthMechanism::class)
@@ -73,10 +69,11 @@ class BackendServiceTest extends \Test\TestCase {
}
public function testRegisterBackend() {
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backend = $this->getBackendMock('\Foo\Bar');
+ /** @var \OCA\Files_External\Lib\Backend\Backend|\PHPUnit_Framework_MockObject_MockObject $backendAlias */
$backendAlias = $this->getMockBuilder(Backend::class)
->disableOriginalConstructor()
->getMock();
@@ -100,11 +97,12 @@ class BackendServiceTest extends \Test\TestCase {
}
public function testBackendProvider() {
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backend1 = $this->getBackendMock('\Foo\Bar');
$backend2 = $this->getBackendMock('\Bar\Foo');
+ /** @var IBackendProvider|\PHPUnit_Framework_MockObject_MockObject $providerMock */
$providerMock = $this->createMock(IBackendProvider::class);
$providerMock->expects($this->once())
->method('getBackends')
@@ -118,11 +116,12 @@ class BackendServiceTest extends \Test\TestCase {
}
public function testAuthMechanismProvider() {
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backend1 = $this->getAuthMechanismMock('\Foo\Bar');
$backend2 = $this->getAuthMechanismMock('\Bar\Foo');
+ /** @var IAuthMechanismProvider|\PHPUnit_Framework_MockObject_MockObject $providerMock */
$providerMock = $this->createMock(IAuthMechanismProvider::class);
$providerMock->expects($this->once())
->method('getAuthMechanisms')
@@ -136,18 +135,20 @@ class BackendServiceTest extends \Test\TestCase {
}
public function testMultipleBackendProviders() {
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backend1a = $this->getBackendMock('\Foo\Bar');
$backend1b = $this->getBackendMock('\Bar\Foo');
$backend2 = $this->getBackendMock('\Dead\Beef');
+ /** @var IBackendProvider|\PHPUnit_Framework_MockObject_MockObject $provider1Mock */
$provider1Mock = $this->createMock(IBackendProvider::class);
$provider1Mock->expects($this->once())
->method('getBackends')
->willReturn([$backend1a, $backend1b]);
$service->registerBackendProvider($provider1Mock);
+ /** @var IBackendProvider|\PHPUnit_Framework_MockObject_MockObject $provider2Mock */
$provider2Mock = $this->createMock(IBackendProvider::class);
$provider2Mock->expects($this->once())
->method('getBackends')
@@ -169,7 +170,7 @@ class BackendServiceTest extends \Test\TestCase {
['files_external', 'user_mounting_backends', '', 'identifier:\User\Mount\Allowed,identifier_alias']
]));
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backendAllowed = $this->getBackendMock('\User\Mount\Allowed');
$backendAllowed->expects($this->never())
@@ -193,7 +194,7 @@ class BackendServiceTest extends \Test\TestCase {
}
public function testGetAvailableBackends() {
- $service = new BackendService($this->config, $this->l10n);
+ $service = new BackendService($this->config);
$backendAvailable = $this->getBackendMock('\Backend\Available');
$backendAvailable->expects($this->once())
@@ -216,5 +217,50 @@ class BackendServiceTest extends \Test\TestCase {
$this->assertArrayNotHasKey('identifier:\Backend\NotAvailable', $availableBackends);
}
+ public function invalidConfigPlaceholderProvider() {
+ return [
+ [['@user']],
+ [['$user']],
+ [['hællo']],
+ [['spa ce']],
+ [['yo\o']],
+ [['<script>…</script>']],
+ [['xxyoloxx', 'invÆlid']],
+ [['tautology', 'tautology']],
+ [['tautology2', 'TAUTOLOGY2']],
+ ];
+ }
+
+ /**
+ * @dataProvider invalidConfigPlaceholderProvider
+ * @expectedException \RuntimeException
+ */
+ public function testRegisterConfigHandlerInvalid(array $placeholders) {
+ $service = new BackendService($this->config);
+ $mock = $this->createMock(IConfigHandler::class);
+ $cb = function () use ($mock) { return $mock; };
+ foreach ($placeholders as $placeholder) {
+ $service->registerConfigHandler($placeholder, $cb);
+ }
+ }
+
+ public function testConfigHandlers() {
+ $service = new BackendService($this->config);
+ $mock = $this->createMock(IConfigHandler::class);
+ $mock->expects($this->exactly(3))
+ ->method('handle');
+ $cb = function () use ($mock) { return $mock; };
+ $service->registerConfigHandler('one', $cb);
+ $service->registerConfigHandler('2', $cb);
+ $service->registerConfigHandler('Three', $cb);
+
+ /** @var IConfigHandler[] $handlers */
+ $handlers = $service->getConfigHandlers();
+
+ foreach ($handlers as $handler) {
+ $handler->handle('Something');
+ }
+ }
+
}
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
index f371ef9efb3..5afd928301a 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 e25b7ee3126..fadbc701ec0 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 23819055be4..d40df6e4836 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/js/wizard/wizardTabAdvanced.js b/apps/user_ldap/js/wizard/wizardTabAdvanced.js
index 9302104b1f6..1545147f64c 100644
--- a/apps/user_ldap/js/wizard/wizardTabAdvanced.js
+++ b/apps/user_ldap/js/wizard/wizardTabAdvanced.js
@@ -120,7 +120,11 @@ OCA = OCA || {};
home_folder_naming_rule: {
$element: $('#home_folder_naming_rule'),
setMethod: 'setHomeFolderAttribute'
- }
+ },
+ ldap_ext_storage_home_attribute: {
+ $element: $('#ldap_ext_storage_home_attribute'),
+ setMethod: 'setExternalStorageHomeAttribute'
+ },
};
this.setManagedItems(items);
},
@@ -327,6 +331,15 @@ OCA = OCA || {};
},
/**
+ * sets the external storage home attribute
+ *
+ * @param {string} attribute
+ */
+ setExternalStorageHomeAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_ext_storage_home_attribute.$element, attribute);
+ },
+
+ /**
* sets the quota attribute
*
* @param {string} attribute
diff --git a/apps/user_ldap/lib/AppInfo/Application.php b/apps/user_ldap/lib/AppInfo/Application.php
index 59d7cdb4924..7e0c3534488 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 c912d30b49b..ee77702a090 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 7becf311a22..ba393dffc12 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 00000000000..540b65c1820
--- /dev/null
+++ b/apps/user_ldap/lib/Exceptions/AttributeNotSet.php
@@ -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 {}
diff --git a/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php b/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php
new file mode 100644
index 00000000000..98a3cc71263
--- /dev/null
+++ b/apps/user_ldap/lib/Handler/ExtStorageConfigHandler.php
@@ -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);
+ }
+}
diff --git a/apps/user_ldap/lib/User/Manager.php b/apps/user_ldap/lib/User/Manager.php
index 6185c0da45c..046b42551b6 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 0d8f993746f..d68d8b35d14 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();
@@ -617,6 +625,47 @@ class User {
}
/**
+ * @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
*
* @param array $params
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 6d69d0aeb15..7c41363c635 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -108,6 +108,7 @@ style('user_ldap', 'settings');
<p><label for="ldap_quota_def"><?php p($l->t('Quota Default'));?></label><input type="text" id="ldap_quota_def" name="ldap_quota_def" data-default="<?php p($_['ldap_quota_def_default']); ?>" title="<?php p($l->t('Override default quota for LDAP users who do not have a quota set in the Quota Field.'));?>" /></p>
<p><label for="ldap_email_attr"><?php p($l->t('Email Field'));?></label><input type="text" id="ldap_email_attr" name="ldap_email_attr" data-default="<?php p($_['ldap_email_attr_default']); ?>" title="<?php p($l->t('Set the user\'s email from their LDAP attribute. Leave it empty for default behaviour.'));?>" /></p>
<p><label for="home_folder_naming_rule"><?php p($l->t('User Home Folder Naming Rule'));?></label><input type="text" id="home_folder_naming_rule" name="home_folder_naming_rule" title="<?php p($l->t('Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute.'));?>" data-default="<?php p($_['home_folder_naming_rule_default']); ?>" /></p>
+ <p><label for="ldap_ext_storage_home_attribute"> <?php p($l->t('"$home" Placeholder Field')); ?></label><input type="text" id="ldap_ext_storage_home_attribute" name="ldap_ext_storage_home_attribute" title="<?php p($l->t('$home in an external storage configuration will replaced with the value of the specified attribute')); ?>" data-default="<?php p($_['ldap_ext_storage_home_attribute_default']); ?>"></p>
</div>
</div>
<?php print_unescaped($_['settingControls']); ?>
diff --git a/apps/user_ldap/tests/ConfigurationTest.php b/apps/user_ldap/tests/ConfigurationTest.php
index ab1312860fa..6e45f163867 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 6ff9defe47b..f99100789d8 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'],