Merge pull request #14174 from nextcloud/feature/noid/extstorage-mountconfighandler
Mount configuration handlers for external storages
This commit is contained in:
commit
bfd61d849f
|
@ -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'
|
||||
);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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'];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
@ -326,6 +330,15 @@ OCA = OCA || {};
|
|||
this.setElementValue(this.managedItems.ldap_email_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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']); ?>
|
||||
|
|
|
@ -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'],
|
||||
|
|
Loading…
Reference in New Issue