Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@arthur-schiwon.de>2019-02-12 01:18:08 +0300
committerArthur Schiwon <blizzz@arthur-schiwon.de>2019-02-14 17:20:48 +0300
commita26bcd8e8fa11870c9192d24c73fbef3ef6112de (patch)
treeb36e72d8eb27e17dd029478fce723b72670abd3c /apps/files_external/lib
parenta80bae398ab2dd5683ca7c80eb7648e5c3dce426 (diff)
files_external: allow to register config handlers for flexible placeholders
* BackendService (directly accessable via \OC_Server) offers registerConfigHandler * SimpleSubstitutionTrait brings reusable logic for simple string replacments * internal $user replacement mechanism was migrated Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Diffstat (limited to 'apps/files_external/lib')
-rw-r--r--apps/files_external/lib/AppInfo/Application.php12
-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/Service/BackendService.php68
-rw-r--r--apps/files_external/lib/config.php36
7 files changed, 282 insertions, 18 deletions
diff --git a/apps/files_external/lib/AppInfo/Application.php b/apps/files_external/lib/AppInfo/Application.php
index 8eebc550d09..5b12377fdb8 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,9 +82,13 @@ 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
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..9abbfe0df22
--- /dev/null
+++ b/apps/files_external/lib/Config/SimpleSubstitutionTrait.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 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/Service/BackendService.php b/apps/files_external/lib/Service/BackendService.php
index bd6c525f319..05bda1998da 100644
--- a/apps/files_external/lib/Service/BackendService.php
+++ b/apps/files_external/lib/Service/BackendService.php
@@ -23,6 +23,7 @@
namespace OCA\Files_External\Service;
+use OCA\Files_External\Config\IConfigHandler;
use \OCP\IConfig;
use \OCA\Files_External\Lib\Backend\Backend;
@@ -67,6 +68,11 @@ class BackendService {
/** @var IAuthMechanismProvider[] */
private $authMechanismProviders = [];
+ /** @var callable[] */
+ private $configHandlerLoaders = [];
+
+ private $configHandlers = [];
+
/**
* @param IConfig $config
*/
@@ -280,4 +286,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[] = $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..2078abc029e 100644
--- a/apps/files_external/lib/config.php
+++ b/apps/files_external/lib/config.php
@@ -35,6 +35,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 +106,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 +115,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 +201,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 +239,7 @@ class OC_Mount_Config {
return StorageNotAvailableException::STATUS_SUCCESS;
}
foreach ($options as &$option) {
- $option = self::setUserVars(OCP\User::getUser(), $option);
+ $option = self::substitutePlaceholdersInConfig($option);
}
if (class_exists($class)) {
try {