From 37beb58c6f395523d8e2934870c5f52a8c6f6df0 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Tue, 11 Aug 2015 18:45:07 +0100 Subject: [PATCH 01/10] Introduce BackendService for managing external storage backends Backends are registered to the BackendService through new data structures: Backends are concrete classes, deriving from \OCA\Files_External\Lib\Backend\Backend. During construction, the various configuration parameters of the Backend can be set, in a design similar to Symfony Console. DefinitionParameter stores a parameter configuration for an external storage: name of parameter, human-readable name, type of parameter (text, password, hidden, checkbox), flags (optional or not). Storages in the StoragesController now get their parameters validated server-side (fixes a TODO). --- apps/files_external/appinfo/app.php | 13 +- apps/files_external/appinfo/application.php | 15 + .../controller/globalstoragescontroller.php | 41 +-- .../controller/storagescontroller.php | 69 ++++- .../controller/userstoragescontroller.php | 42 ++- apps/files_external/lib/backend/backend.php | 94 +++++++ apps/files_external/lib/config.php | 257 +++++------------- .../lib/config/configadapter.php | 14 +- .../lib/definitionparameter.php | 179 ++++++++++++ apps/files_external/lib/dependencytrait.php | 86 ++++++ .../lib/frontenddefinitiontrait.php | 147 ++++++++++ apps/files_external/lib/missingdependency.php | 64 +++++ apps/files_external/lib/personalmount.php | 45 ++- apps/files_external/lib/prioritytrait.php | 60 ++++ apps/files_external/lib/storageconfig.php | 45 ++- .../lib/storagemodifiertrait.php | 51 ++++ apps/files_external/lib/visibilitytrait.php | 129 +++++++++ apps/files_external/personal.php | 34 +-- .../files_external/service/backendservice.php | 170 ++++++++++++ .../service/storagesservice.php | 68 ++++- .../service/userstoragesservice.php | 4 + apps/files_external/settings.php | 51 +--- apps/files_external/templates/settings.php | 215 +++++++++------ .../globalstoragescontrollertest.php | 4 +- .../controller/storagescontrollertest.php | 61 ++++- .../controller/userstoragescontrollertest.php | 51 ++-- .../tests/dynamicmountconfig.php | 104 ------- .../service/globalstoragesservicetest.php | 26 +- .../tests/service/storagesservicetest.php | 47 +++- .../tests/service/userstoragesservicetest.php | 4 +- .../tests/storageconfigtest.php | 8 +- 31 files changed, 1639 insertions(+), 559 deletions(-) create mode 100644 apps/files_external/lib/backend/backend.php create mode 100644 apps/files_external/lib/definitionparameter.php create mode 100644 apps/files_external/lib/dependencytrait.php create mode 100644 apps/files_external/lib/frontenddefinitiontrait.php create mode 100644 apps/files_external/lib/missingdependency.php create mode 100644 apps/files_external/lib/prioritytrait.php create mode 100644 apps/files_external/lib/storagemodifiertrait.php create mode 100644 apps/files_external/lib/visibilitytrait.php create mode 100644 apps/files_external/service/backendservice.php delete mode 100644 apps/files_external/tests/dynamicmountconfig.php diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 66897eba3d..37a489535e 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -30,9 +30,6 @@ * along with this program. If not, see * */ -$app = new \OCA\Files_external\Appinfo\Application(); - -$l = \OC::$server->getL10N('files_external'); OC::$CLASSPATH['OC\Files\Storage\StreamWrapper'] = 'files_external/lib/streamwrapper.php'; OC::$CLASSPATH['OC\Files\Storage\FTP'] = 'files_external/lib/ftp.php'; @@ -50,6 +47,11 @@ OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php'; require_once __DIR__ . '/../3rdparty/autoload.php'; +$app = new \OCA\Files_external\Appinfo\Application(); +$appContainer = $app->getContainer(); + +$l = \OC::$server->getL10N('files_external'); + OCP\App::registerAdmin('files_external', 'settings'); if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') { OCP\App::registerPersonal('files_external', 'personal'); @@ -63,6 +65,9 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == ' "name" => $l->t('External storage') ]); +// Teach OC_Mount_Config about the AppFramework +\OC_Mount_Config::initApp($appContainer); + // connecting hooks OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook'); OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login'); @@ -237,5 +242,5 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP_Key', [ 'custom' => 'sftp_key', ] ); -$mountProvider = new \OCA\Files_External\Config\ConfigAdapter(); +$mountProvider = $appContainer->query('OCA\Files_External\Config\ConfigAdapter'); \OC::$server->getMountProviderCollection()->registerProvider($mountProvider); diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php index d77a302466..b8b1fdaa27 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -3,6 +3,7 @@ * @author Morris Jobke * @author Ross Nicoll * @author Vincent Petry + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -26,6 +27,9 @@ namespace OCA\Files_External\AppInfo; use \OCA\Files_External\Controller\AjaxController; use \OCP\AppFramework\App; use \OCP\IContainer; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\BackendConfig; +use \OCA\Files_External\Lib\BackendParameter; /** * @package OCA\Files_External\Appinfo @@ -45,5 +49,16 @@ class Application extends App { $c->query('Request') ); }); + + $this->loadBackends(); } + + /** + * Load storage backends provided by this app + */ + protected function loadBackends() { + $container = $this->getContainer(); + $service = $container->query('OCA\\Files_External\\Service\\BackendService'); + } + } diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index 33f870d48e..11f7fd6afa 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -81,14 +81,18 @@ class GlobalStoragesController extends StoragesController { $applicableGroups, $priority ) { - $newStorage = new StorageConfig(); - $newStorage->setMountPoint($mountPoint); - $newStorage->setBackendClass($backendClass); - $newStorage->setBackendOptions($backendOptions); - $newStorage->setMountOptions($mountOptions); - $newStorage->setApplicableUsers($applicableUsers); - $newStorage->setApplicableGroups($applicableGroups); - $newStorage->setPriority($priority); + $newStorage = $this->createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + if ($newStorage instanceof DataResponse) { + return $newStorage; + } $response = $this->validate($newStorage); if (!empty($response)) { @@ -129,14 +133,19 @@ class GlobalStoragesController extends StoragesController { $applicableGroups, $priority ) { - $storage = new StorageConfig($id); - $storage->setMountPoint($mountPoint); - $storage->setBackendClass($backendClass); - $storage->setBackendOptions($backendOptions); - $storage->setMountOptions($mountOptions); - $storage->setApplicableUsers($applicableUsers); - $storage->setApplicableGroups($applicableGroups); - $storage->setPriority($priority); + $storage = $this->createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + if ($storage instanceof DataResponse) { + return $storage; + } + $storage->setId($id); $response = $this->validate($storage); if (!empty($response)) { diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index c09ceacc7d..c653b51bf8 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -32,6 +32,7 @@ use \OCP\AppFramework\Http; use \OCA\Files_external\Service\StoragesService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\Backend\Backend; /** * Base class for storages controllers @@ -71,6 +72,48 @@ abstract class StoragesController extends Controller { $this->service = $storagesService; } + /** + * Create a storage from its parameters + * + * @param string $mountPoint storage mount point + * @param string $backendClass backend class name + * @param array $backendOptions backend-specific options + * @param array|null $mountOptions mount-specific options + * @param array|null $applicableUsers users for which to mount the storage + * @param array|null $applicableGroups groups for which to mount the storage + * @param int|null $priority priority + * + * @return StorageConfig|DataResponse + */ + protected function createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions = null, + $applicableUsers = null, + $applicableGroups = null, + $priority = null + ) { + try { + return $this->service->createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + } catch (\InvalidArgumentException $e) { + return new DataResponse( + [ + 'message' => (string)$this->l10n->t('Invalid backend class "%s"', [$backendClass]) + ], + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + } + /** * Validate storage config * @@ -89,14 +132,24 @@ abstract class StoragesController extends Controller { ); } - // TODO: validate that other attrs are set - - $backends = \OC_Mount_Config::getBackends(); - if (!isset($backends[$storage->getBackendClass()])) { + /** @var Backend */ + $backend = $storage->getBackend(); + if (!$backend || $backend->checkDependencies()) { // invalid backend return new DataResponse( array( - 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass())) + 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [ + $storage->getBackend()->getClass() + ]) + ), + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + if (!$backend->validateStorage($storage)) { + // unsatisfied parameters + return new DataResponse( + array( + 'message' => (string)$this->l10n->t('Unsatisfied backend parameters') ), Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -114,10 +167,14 @@ abstract class StoragesController extends Controller { * @param StorageConfig $storage storage configuration */ protected function updateStorageStatus(StorageConfig &$storage) { + /** @var Backend */ + $backend = $storage->getBackend(); + $backend->manipulateStorageConfig($storage); + // update status (can be time-consuming) $storage->setStatus( \OC_Mount_Config::getBackendStatus( - $storage->getBackendClass(), + $storage->getBackend()->getStorageClass(), $storage->getBackendOptions(), false ) diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index f5d22e5caa..5a5bff7ba7 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -30,8 +30,10 @@ use \OCP\AppFramework\Http\DataResponse; use \OCP\AppFramework\Controller; use \OCP\AppFramework\Http; use \OCA\Files_external\Service\UserStoragesService; +use \OCA\Files_External\Service\BackendService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\Backend\Backend; /** * User storages controller @@ -69,17 +71,20 @@ class UserStoragesController extends StoragesController { protected function validate(StorageConfig $storage) { $result = parent::validate($storage); - if ($result != null) { + if ($result !== null) { return $result; } // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage and other disabled backends - $allowedBackends = \OC_Mount_Config::getPersonalBackends(); - if (!isset($allowedBackends[$storage->getBackendClass()])) { + /** @var Backend */ + $backend = $storage->getBackend(); + if (!$backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { return new DataResponse( array( - 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass())) + 'message' => (string)$this->l10n->t('Admin-only storage backend "%s"', [ + $storage->getBackend()->getClass() + ]) ), Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -117,11 +122,15 @@ class UserStoragesController extends StoragesController { $backendOptions, $mountOptions ) { - $newStorage = new StorageConfig(); - $newStorage->setMountPoint($mountPoint); - $newStorage->setBackendClass($backendClass); - $newStorage->setBackendOptions($backendOptions); - $newStorage->setMountOptions($mountOptions); + $newStorage = $this->createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions + ); + if ($newStorage instanceOf DataResponse) { + return $newStorage; + } $response = $this->validate($newStorage); if (!empty($response)) { @@ -157,11 +166,16 @@ class UserStoragesController extends StoragesController { $backendOptions, $mountOptions ) { - $storage = new StorageConfig($id); - $storage->setMountPoint($mountPoint); - $storage->setBackendClass($backendClass); - $storage->setBackendOptions($backendOptions); - $storage->setMountOptions($mountOptions); + $storage = $this->createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions + ); + if ($storage instanceOf DataResponse) { + return $storage; + } + $storage->setId($id); $response = $this->validate($storage); if (!empty($response)) { diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php new file mode 100644 index 0000000000..e7cd27a1d6 --- /dev/null +++ b/apps/files_external/lib/backend/backend.php @@ -0,0 +1,94 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\FrontendDefinitionTrait; +use \OCA\Files_External\Lib\PriorityTrait; +use \OCA\Files_External\Lib\DependencyTrait; +use \OCA\Files_External\Lib\StorageModifierTrait; + +/** + * Storage backend + */ +class Backend implements \JsonSerializable { + + use VisibilityTrait; + use FrontendDefinitionTrait; + use PriorityTrait; + use DependencyTrait; + use StorageModifierTrait; + + /** @var string storage class */ + private $storageClass; + + /** + * @return string + */ + public function getClass() { + // return storage class for legacy compat + return $this->getStorageClass(); + } + + /** + * @return string + */ + public function getStorageClass() { + return $this->storageClass; + } + + /** + * @param string $class + * @return self + */ + public function setStorageClass($class) { + $this->storageClass = $class; + return $this; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerialize() { + $data = $this->jsonSerializeDefinition(); + + $data['backend'] = $data['name']; // legacy compat + $data['priority'] = $this->getPriority(); + + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorage(StorageConfig $storage) { + return $this->validateStorageDefinition($storage); + } + +} + diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 8fcf39cc76..11dec94621 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -32,6 +32,9 @@ */ use phpseclib\Crypt\AES; +use \OCP\AppFramework\IAppContainer; +use \OCA\Files_External\Lib\BackendConfig; +use \OCA\Files_External\Service\BackendService; /** * Class to configure mount.json globally and for users @@ -51,71 +54,19 @@ class OC_Mount_Config { // whether to skip backend test (for unit tests, as this static class is not mockable) public static $skipTest = false; - private static $backends = array(); + /** @var IAppContainer */ + private static $appContainer; /** - * @param string $class - * @param array $definition - * @return bool - */ - public static function registerBackend($class, $definition) { - if (!isset($definition['backend'])) { - return false; - } - - OC_Mount_Config::$backends[$class] = $definition; - return true; - } - - /** - * Setup backends + * Teach OC_Mount_Config about the AppFramework * - * @return array of previously registered backends + * @param IAppContainer $appContainer */ - public static function setUp($backends = array()) { - $backup = self::$backends; - self::$backends = $backends; - - return $backup; + public static function initApp(IAppContainer $appContainer) { + self::$appContainer = $appContainer; } - /** - * Get details on each of the external storage backends, used for the mount config UI - * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded - * If the configuration parameter should be secret, add a '*' to the beginning of the value - * If the configuration parameter is a boolean, add a '!' to the beginning of the value - * If the configuration parameter is optional, add a '&' to the beginning of the value - * If the configuration parameter is hidden, add a '#' to the beginning of the value - * - * @return array - */ - public static function getBackends() { - $sortFunc = function ($a, $b) { - return strcasecmp($a['backend'], $b['backend']); - }; - - $backEnds = array(); - - foreach (OC_Mount_Config::$backends as $class => $backend) { - if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) { - if (!method_exists($class, 'checkDependencies')) { - \OCP\Util::writeLog('files_external', - "Backend class $class has dependencies but doesn't provide method checkDependencies()", - \OCP\Util::DEBUG); - continue; - } elseif ($class::checkDependencies() !== true) { - continue; - } - } - $backEnds[$class] = $backend; - } - - uasort($backEnds, $sortFunc); - - return $backEnds; - } - - /** + /* * Hook that mounts the given user's visible mount points * * @param array $data @@ -151,14 +102,14 @@ class OC_Mount_Config { /** * Returns the mount points for the given user. * The mount point is relative to the data directory. + * TODO: Move me into StoragesService * * @param string $user user * @return array of mount point string as key, mountpoint config as value */ public static function getAbsoluteMountPoints($user) { $mountPoints = array(); - - $backends = self::getBackends(); + $backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService'); // Load system mount points $mountConfig = self::readData(); @@ -166,18 +117,20 @@ class OC_Mount_Config { // Global mount points (is this redundant?) if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) { foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) { + $backend = $backendService->getBackend($options['class']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; + $options['priority'] = $backend->getPriority(); } + // Override if priority greater if ((!isset($mountPoints[$mountPoint])) || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) ) { $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backends[$options['class']]['backend']; + $options['backend'] = $backend->getText(); $mountPoints[$mountPoint] = $options; } } @@ -190,10 +143,11 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } + $backend = $backendService->getBackend($options['class']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; + $options['priority'] = $backend->getPriority(); } // Override if priority greater @@ -201,7 +155,7 @@ class OC_Mount_Config { || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) ) { $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backends[$options['class']]['backend']; + $options['backend'] = $backend->getText(); $mountPoints[$mountPoint] = $options; } } @@ -215,10 +169,11 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } + $backend = $backendService->getBackend($options['class']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; + $options['priority'] = $backend->getPriority(); } // Override if priority greater or if priority type different @@ -227,7 +182,7 @@ class OC_Mount_Config { || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_GROUP) ) { $options['priority_type'] = self::MOUNT_TYPE_GROUP; - $options['backend'] = $backends[$options['class']]['backend']; + $options['backend'] = $backend->getText(); $mountPoints[$mountPoint] = $options; } } @@ -243,10 +198,11 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } + $backend = $backendService->getBackend($options['class']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; + $options['priority'] = $backend->getPriority(); } // Override if priority greater or if priority type different @@ -255,7 +211,7 @@ class OC_Mount_Config { || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_USER) ) { $options['priority_type'] = self::MOUNT_TYPE_USER; - $options['backend'] = $backends[$options['class']]['backend']; + $options['backend'] = $backend->getText(); $mountPoints[$mountPoint] = $options; } } @@ -263,19 +219,18 @@ class OC_Mount_Config { } } - $personalBackends = self::getPersonalBackends(); - // Load personal mount points $mountConfig = self::readData($user); if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) { foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) { - if (isset($personalBackends[$options['class']])) { + $backend = $backendService->getBackend($options['class']); + if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { $options['personal'] = true; $options['options'] = self::decryptPasswords($options['options']); // Always override previous config $options['priority_type'] = self::MOUNT_TYPE_PERSONAL; - $options['backend'] = $backends[$options['class']]['backend']; + $options['backend'] = $backend->getText(); $mountPoints[$mountPoint] = $options; } } @@ -304,43 +259,6 @@ class OC_Mount_Config { return $input; } - - /** - * Get details on each of the external storage backends, used for the mount config UI - * Some backends are not available as a personal backend, f.e. Local and such that have - * been disabled by the admin. - * - * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded - * If the configuration parameter should be secret, add a '*' to the beginning of the value - * If the configuration parameter is a boolean, add a '!' to the beginning of the value - * If the configuration parameter is optional, add a '&' to the beginning of the value - * If the configuration parameter is hidden, add a '#' to the beginning of the value - * - * @return array - */ - public static function getPersonalBackends() { - - // Check whether the user has permissions to add personal storage backends - // return an empty array if this is not the case - if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') { - return array(); - } - - $backEnds = self::getBackends(); - - // Remove local storage and other disabled storages - unset($backEnds['\OC\Files\Storage\Local']); - - $allowedBackEnds = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); - foreach ($backEnds as $backend => $null) { - if (!in_array($backend, $allowedBackEnds)) { - unset($backEnds[$backend]); - } - } - - return $backEnds; - } - /** * Get the system mount points * The returned array is not in the same format as getUserMountPoints() @@ -349,7 +267,7 @@ class OC_Mount_Config { */ public static function getSystemMountPoints() { $mountPoints = self::readData(); - $backends = self::getBackends(); + $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); $system = array(); if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) { foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) { @@ -358,9 +276,10 @@ class OC_Mount_Config { if (strpos($mount['class'], 'OC_Filestorage_') !== false) { $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); } + $backend = $backendService->getBackend($mount['class']); $mount['options'] = self::decryptPasswords($mount['options']); if (!isset($mount['priority'])) { - $mount['priority'] = $backends[$mount['class']]['priority']; + $mount['priority'] = $backend->getPriority(); } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); @@ -368,7 +287,7 @@ class OC_Mount_Config { $config = array( 'class' => $mount['class'], 'mountpoint' => $mountPoint, - 'backend' => $backends[$mount['class']]['backend'], + 'backend' => $backend->getText(), 'priority' => $mount['priority'], 'options' => $mount['options'], 'applicable' => array('groups' => array($group), 'users' => array()) @@ -401,16 +320,17 @@ class OC_Mount_Config { if (strpos($mount['class'], 'OC_Filestorage_') !== false) { $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); } + $backend = $backendService->getBackend($mount['class']); $mount['options'] = self::decryptPasswords($mount['options']); if (!isset($mount['priority'])) { - $mount['priority'] = $backends[$mount['class']]['priority']; + $mount['priority'] = $backend->getPriority(); } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); $config = array( 'class' => $mount['class'], 'mountpoint' => $mountPoint, - 'backend' => $backends[$mount['class']]['backend'], + 'backend' => $backend->getText(), 'priority' => $mount['priority'], 'options' => $mount['options'], 'applicable' => array('groups' => array(), 'users' => array($user)) @@ -447,7 +367,7 @@ class OC_Mount_Config { */ public static function getPersonalMountPoints() { $mountPoints = self::readData(OCP\User::getUser()); - $backEnds = self::getBackends(); + $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); $uid = OCP\User::getUser(); $personal = array(); if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) { @@ -456,12 +376,13 @@ class OC_Mount_Config { if (strpos($mount['class'], 'OC_Filestorage_') !== false) { $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); } + $backend = $backendService->getBackend($mount['class']); $mount['options'] = self::decryptPasswords($mount['options']); $config = array( 'class' => $mount['class'], // Remove '/uid/files/' from mount point 'mountpoint' => substr($mountPoint, strlen($uid) + 8), - 'backend' => $backEnds[$mount['class']]['backend'], + 'backend' => $backend->getText(), 'options' => $mount['options'] ); if (isset($mount['id'])) { @@ -535,7 +456,7 @@ class OC_Mount_Config { $applicable, $isPersonal = false, $priority = null) { - $backends = self::getBackends(); + $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); $relMountPoint = $mountPoint; if ($mountPoint === '' || $mountPoint === '/') { @@ -543,15 +464,15 @@ class OC_Mount_Config { return false; } - if (!isset($backends[$class])) { + $backend = $backendService->getBackend($class); + if (!isset($backend)) { // invalid backend return false; } if ($isPersonal) { // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage and other disabled backends - $allowed_backends = self::getPersonalBackends(); - if ($applicable != OCP\User::getUser() || !isset($allowed_backends[$class])) { + if ($applicable != OCP\User::getUser() || !$backend->isVisibleFor(BackendConfig::VISIBILITY_PERSONAL)) { return false; } $mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/'); @@ -578,13 +499,8 @@ class OC_Mount_Config { // Set default priority if none set if (!isset($mountPoints[$mountType][$applicable][$mountPoint]['priority'])) { - if (isset($backends[$class]['priority'])) { - $mountPoints[$mountType][$applicable][$mountPoint]['priority'] - = $backends[$class]['priority']; - } else { - $mountPoints[$mountType][$applicable][$mountPoint]['priority'] - = 100; - } + $mountPoints[$mountType][$applicable][$mountPoint]['priority'] + = $backend->getPriority(); } self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints); @@ -721,74 +637,35 @@ class OC_Mount_Config { } /** - * check dependencies + * Get backend dependency message + * TODO: move into AppFramework along with templates + * + * @param BackendConfig[] $backends + * @return string */ - public static function checkDependencies() { - $dependencies = array(); - foreach (OC_Mount_Config::$backends as $class => $backend) { - if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) { - $result = $class::checkDependencies(); - if ($result !== true) { - if (!is_array($result)) { - $result = array($result); - } - foreach ($result as $key => $value) { - if (is_numeric($key)) { - OC_Mount_Config::addDependency($dependencies, $value, $backend['backend']); - } else { - OC_Mount_Config::addDependency($dependencies, $key, $backend['backend'], $value); - } - } - } - } - } - - if (count($dependencies) > 0) { - return OC_Mount_Config::generateDependencyMessage($dependencies); - } - return ''; - } - - private static function addDependency(&$dependencies, $module, $backend, $message = null) { - if (!isset($dependencies[$module])) { - $dependencies[$module] = array(); - } - - if ($message === null) { - $dependencies[$module][] = $backend; - } else { - $dependencies[$module][] = array('backend' => $backend, 'message' => $message); - } - } - - private static function generateDependencyMessage($dependencies) { + public static function dependencyMessage($backends) { $l = new \OC_L10N('files_external'); - $dependencyMessage = ''; - foreach ($dependencies as $module => $backends) { - $dependencyGroup = array(); - foreach ($backends as $backend) { - if (is_array($backend)) { - $dependencyMessage .= '
' . $l->t('Note: ') . $backend['message']; - } else { - $dependencyGroup[] = $backend; - } - } + $message = ''; + $dependencyGroups = []; - $dependencyGroupCount = count($dependencyGroup); - if ($dependencyGroupCount > 0) { - $backends = ''; - for ($i = 0; $i < $dependencyGroupCount; $i++) { - if ($i > 0 && $i === $dependencyGroupCount - 1) { - $backends .= ' ' . $l->t('and') . ' '; - } elseif ($i > 0) { - $backends .= ', '; - } - $backends .= '' . $dependencyGroup[$i] . ''; + foreach ($backends as $backend) { + foreach ($backend->checkDependencies() as $dependency) { + if ($message = $dependency->getMessage()) { + $message .= '
' . $l->t('Note: ') . $message; + } else { + $dependencyGroups[$dependency->getDependency()][] = $backend; } - $dependencyMessage .= '
' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends); } } - return $dependencyMessage; + + foreach ($dependencyGroups as $module => $dependants) { + $backends = implode(', ', array_map(function($backend) { + return '' . $backend->getText() . ''; + }, $dependants)); + $message .= '
' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends); + } + + return $message; } /** diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index b5c2ba4fc9..6956de1e74 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -24,14 +24,23 @@ namespace OCA\Files_External\Config; use OC\Files\Mount\MountPoint; use OCP\Files\Storage\IStorageFactory; -use OCA\Files_External\PersonalMount; +use OCA\Files_External\Lib\PersonalMount; use OCP\Files\Config\IMountProvider; use OCP\IUser; +use OCA\Files_external\Service\UserStoragesService; /** * Make the old files_external config work with the new public mount config api */ class ConfigAdapter implements IMountProvider { + + /** + * @param UserStoragesService $userStoragesService + */ + public function __construct(UserStoragesService $userStoragesService) { + $this->userStoragesService = $userStoragesService; + } + /** * Get all mountpoints applicable for the user * @@ -49,7 +58,8 @@ class ConfigAdapter implements IMountProvider { } $mountOptions = isset($options['mountOptions']) ? $options['mountOptions'] : []; if (isset($options['personal']) && $options['personal']) { - $mounts[] = new PersonalMount($options['class'], $mountPoint, $options['options'], $loader, $mountOptions); + $mount = new PersonalMount($this->userStoragesService, $options['id'], $options['class'], $mountPoint, $options['options'], $loader, $mountOptions); + $mounts[] = $mount; } else { $mounts[] = new MountPoint($options['class'], $mountPoint, $options['options'], $loader, $mountOptions); } diff --git a/apps/files_external/lib/definitionparameter.php b/apps/files_external/lib/definitionparameter.php new file mode 100644 index 0000000000..4b560908b6 --- /dev/null +++ b/apps/files_external/lib/definitionparameter.php @@ -0,0 +1,179 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * Parameter for an external storage definition + */ +class DefinitionParameter implements \JsonSerializable { + + /** Value constants */ + const VALUE_TEXT = 0; + const VALUE_BOOLEAN = 1; + const VALUE_PASSWORD = 2; + const VALUE_HIDDEN = 3; + + /** Flag constants */ + const FLAG_NONE = 0; + const FLAG_OPTIONAL = 1; + + /** @var string name of parameter */ + private $name; + + /** @var string human-readable parameter text */ + private $text; + + /** @var int value type, see self::VALUE_* constants */ + private $type = self::VALUE_TEXT; + + /** @var int flags, see self::FLAG_* constants */ + private $flags = self::FLAG_NONE; + + /** + * @param string $name + * @param string $text + */ + public function __construct($name, $text) { + $this->name = $name; + $this->text = $text; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return string + */ + public function getText() { + return $this->text; + } + + /** + * Get value type + * + * @return int + */ + public function getType() { + return $this->type; + } + + /** + * Set value type + * + * @param int $type + * @return self + */ + public function setType($type) { + $this->type = $type; + return $this; + } + + /** + * @return int + */ + public function getFlags() { + return $this->flags; + } + + /** + * @param int $flags + * @return self + */ + public function setFlags($flags) { + $this->flags = $flags; + return $this; + } + + /** + * @param int $flag + * @return self + */ + public function setFlag($flag) { + $this->flags |= $flag; + return $this; + } + + /** + * @param int $flag + * @return bool + */ + public function isFlagSet($flag) { + return (bool) $this->flags & $flag; + } + + /** + * Serialize into JSON for client-side JS + * + * @return string + */ + public function jsonSerialize() { + $prefix = ''; + switch ($this->getType()) { + case self::VALUE_BOOLEAN: + $prefix = '!'; + break; + case self::VALUE_PASSWORD: + $prefix = '*'; + break; + case self::VALUE_HIDDEN: + $prefix = '#'; + break; + } + + switch ($this->getFlags()) { + case self::FLAG_OPTIONAL: + $prefix = '&' . $prefix; + break; + } + + return $prefix . $this->getText(); + } + + /** + * Validate a parameter value against this + * + * @param mixed $value Value to check + * @return bool success + */ + public function validateValue($value) { + if ($this->getFlags() & self::FLAG_OPTIONAL) { + return true; + } + switch ($this->getType()) { + case self::VALUE_BOOLEAN: + if (!is_bool($value)) { + return false; + } + break; + default: + if (empty($value)) { + return false; + } + break; + } + return true; + } +} diff --git a/apps/files_external/lib/dependencytrait.php b/apps/files_external/lib/dependencytrait.php new file mode 100644 index 0000000000..116421eab1 --- /dev/null +++ b/apps/files_external/lib/dependencytrait.php @@ -0,0 +1,86 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Lib\MissingDependency; + +/** + * Trait for objects that have dependencies for use + */ +trait DependencyTrait { + + /** @var callable|null dependency check */ + private $dependencyCheck = null; + + /** + * @return bool + */ + public function hasDependencies() { + return !is_null($this->dependencyCheck); + } + + /** + * @param callable $dependencyCheck + * @return self + */ + public function setDependencyCheck(callable $dependencyCheck) { + $this->dependencyCheck = $dependencyCheck; + return $this; + } + + /** + * Check if object is valid for use + * + * @return MissingDependency[] Unsatisfied dependencies + */ + public function checkDependencies() { + $ret = []; + + if ($this->hasDependencies()) { + $result = call_user_func($this->dependencyCheck); + if ($result !== true) { + if (!is_array($result)) { + $result = [$result]; + } + foreach ($result as $key => $value) { + if (!($value instanceof MissingDependency)) { + $module = null; + $message = null; + if (is_numeric($key)) { + $module = $value; + } else { + $module = $key; + $message = $value; + } + $value = new MissingDependency($module, $this); + $value->setMessage($message); + } + $ret[] = $value; + } + } + } + + return $ret; + } + +} + diff --git a/apps/files_external/lib/frontenddefinitiontrait.php b/apps/files_external/lib/frontenddefinitiontrait.php new file mode 100644 index 0000000000..4b826372d2 --- /dev/null +++ b/apps/files_external/lib/frontenddefinitiontrait.php @@ -0,0 +1,147 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\StorageConfig; + +/** + * Trait for objects that have a frontend representation + */ +trait FrontendDefinitionTrait { + + /** @var string human-readable mechanism name */ + private $text; + + /** @var DefinitionParameter[] parameters for mechanism */ + private $parameters = []; + + /** @var string|null custom JS */ + private $customJs = null; + + /** + * @return string + */ + public function getText() { + return $this->text; + } + + /** + * @param string $text + * @return self + */ + public function setText($text) { + $this->text = $text; + return $this; + } + + /** + * @param FrontendDefinitionTrait $a + * @param FrontendDefinitionTrait $b + * @return int + */ + public static function lexicalCompare(FrontendDefinitionTrait $a, FrontendDefinitionTrait $b) { + return strcmp($a->getText(), $b->getText()); + } + + /** + * @return DefinitionParameter[] + */ + public function getParameters() { + return $this->parameters; + } + + /** + * @param DefinitionParameter[] $parameters + * @return self + */ + public function addParameters(array $parameters) { + foreach ($parameters as $parameter) { + $this->addParameter($parameter); + } + return $this; + } + + /** + * @param DefinitionParameter $parameter + * @return self + */ + public function addParameter(DefinitionParameter $parameter) { + $this->parameters[$parameter->getName()] = $parameter; + return $this; + } + + /** + * @return string|null + */ + public function getCustomJs() { + return $this->customJs; + } + + /** + * @param string $custom + * @return self + */ + public function setCustomJs($custom) { + $this->customJs = $custom; + return $this; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerializeDefinition() { + $configuration = []; + foreach ($this->getParameters() as $parameter) { + $configuration[$parameter->getName()] = $parameter; + } + + $data = [ + 'name' => $this->getText(), + 'configuration' => $configuration, + ]; + if (isset($this->customJs)) { + $data['custom'] = $this->getCustomJs(); + } + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorageDefinition(StorageConfig $storage) { + $options = $storage->getBackendOptions(); + foreach ($this->getParameters() as $name => $parameter) { + $value = isset($options[$name]) ? $options[$name] : null; + if (!$parameter->validateValue($value)) { + return false; + } + } + return true; + } + +} diff --git a/apps/files_external/lib/missingdependency.php b/apps/files_external/lib/missingdependency.php new file mode 100644 index 0000000000..9b25aeacc9 --- /dev/null +++ b/apps/files_external/lib/missingdependency.php @@ -0,0 +1,64 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * External storage backend dependency + */ +class MissingDependency { + + /** @var string */ + private $dependency; + + /** @var string|null Custom message */ + private $message = null; + + /** + * @param string $dependency + */ + public function __construct($dependency) { + $this->dependency = $dependency; + } + + /** + * @return string + */ + public function getDependency() { + return $this->dependency; + } + + /** + * @return string|null + */ + public function getMessage() { + return $this->message; + } + + /** + * @param string $message + * @return self + */ + public function setMessage($message) { + $this->message = $message; + return $this; + } +} diff --git a/apps/files_external/lib/personalmount.php b/apps/files_external/lib/personalmount.php index bbffc95864..d177f1a1ad 100644 --- a/apps/files_external/lib/personalmount.php +++ b/apps/files_external/lib/personalmount.php @@ -20,15 +20,45 @@ * */ -namespace OCA\Files_External; +namespace OCA\Files_External\Lib; use OC\Files\Mount\MountPoint; use OC\Files\Mount\MoveableMount; +use OCA\Files_External\Service\UserStoragesService; /** * Person mount points can be moved by the user */ class PersonalMount extends MountPoint implements MoveableMount { + /** @var UserStoragesService */ + protected $storagesService; + + /** @var int */ + protected $storageId; + + /** + * @param UserStoragesService $storagesService + * @param int $storageId + * @param string|\OC\Files\Storage\Storage $storage + * @param string $mountpoint + * @param array $arguments (optional) configuration for the storage backend + * @param \OCP\Files\Storage\IStorageFactory $loader + * @param array $mountOptions mount specific options + */ + public function __construct( + UserStoragesService $storagesService, + $storageId, + $storage, + $mountpoint, + $arguments = null, + $loader = null, + $mountOptions = null + ) { + parent::__construct($storage, $mountpoint, $arguments, $loader, $mountOptions); + $this->storagesService = $storagesService; + $this->storageId = $storageId; + } + /** * Move the mount point to $target * @@ -36,9 +66,13 @@ class PersonalMount extends MountPoint implements MoveableMount { * @return bool */ public function moveMount($target) { - $result = \OC_Mount_Config::movePersonalMountPoint($this->getMountPoint(), $target, \OC_Mount_Config::MOUNT_TYPE_USER); + $storage = $this->storagesService->getStorage($this->storageId); + // remove "/$user/files" prefix + $targetParts = explode('/', trim($target, '/'), 3); + $storage->setMountPoint($targetParts[2]); + $this->storagesService->updateStorage($storage); $this->setMountPoint($target); - return $result; + return true; } /** @@ -47,8 +81,7 @@ class PersonalMount extends MountPoint implements MoveableMount { * @return bool */ public function removeMount() { - $user = \OCP\User::getUser(); - $relativeMountPoint = substr($this->getMountPoint(), strlen('/' . $user . '/files/')); - return \OC_Mount_Config::removeMountPoint($relativeMountPoint, \OC_Mount_Config::MOUNT_TYPE_USER, $user , true); + $this->storagesService->removeStorage($this->storageId); + return true; } } diff --git a/apps/files_external/lib/prioritytrait.php b/apps/files_external/lib/prioritytrait.php new file mode 100644 index 0000000000..22f9fe275d --- /dev/null +++ b/apps/files_external/lib/prioritytrait.php @@ -0,0 +1,60 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement priority mechanics for a configuration class + */ +trait PriorityTrait { + + /** @var int initial priority */ + protected $priority = BackendService::PRIORITY_DEFAULT; + + /** + * @return int + */ + public function getPriority() { + return $this->priority; + } + + /** + * @param int $priority + * @return self + */ + public function setPriority($priority) { + $this->priority = $priority; + return $this; + } + + /** + * @param PriorityTrait $a + * @param PriorityTrait $b + * @return int + */ + public static function priorityCompare(PriorityTrait $a, PriorityTrait $b) { + return ($a->getPriority() - $b->getPriority()); + } + +} + diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php index 92c27701d8..cf8271ff4e 100644 --- a/apps/files_external/lib/storageconfig.php +++ b/apps/files_external/lib/storageconfig.php @@ -21,6 +21,8 @@ namespace OCA\Files_external\Lib; +use \OCA\Files_External\Lib\Backend\Backend; + /** * External storage configuration */ @@ -34,11 +36,11 @@ class StorageConfig implements \JsonSerializable { private $id; /** - * Backend class name + * Backend * - * @var string + * @var Backend */ - private $backendClass; + private $backend; /** * Backend options @@ -138,21 +140,17 @@ class StorageConfig implements \JsonSerializable { } /** - * Returns the external storage backend class name - * - * @return string external storage backend class name + * @return Backend */ - public function getBackendClass() { - return $this->backendClass; + public function getBackend() { + return $this->backend; } /** - * Sets the external storage backend class name - * - * @param string external storage backend class name + * @param Backend */ - public function setBackendClass($backendClass) { - $this->backendClass = $backendClass; + public function setBackend(Backend $backend) { + $this->backend= $backend; } /** @@ -173,6 +171,25 @@ class StorageConfig implements \JsonSerializable { $this->backendOptions = $backendOptions; } + /** + * @param string $key + * @return mixed + */ + public function getBackendOption($key) { + if (isset($this->backendOptions[$key])) { + return $this->backendOptions[$key]; + } + return null; + } + + /** + * @param string $key + * @param mixed $value + */ + public function setBackendOption($key, $value) { + $this->backendOptions[$key] = $value; + } + /** * Returns the mount priority * @@ -283,7 +300,7 @@ class StorageConfig implements \JsonSerializable { $result['id'] = $this->id; } $result['mountPoint'] = $this->mountPoint; - $result['backendClass'] = $this->backendClass; + $result['backendClass'] = $this->backend->getClass(); $result['backendOptions'] = $this->backendOptions; if (!is_null($this->priority)) { $result['priority'] = $this->priority; diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php new file mode 100644 index 0000000000..f78116103d --- /dev/null +++ b/apps/files_external/lib/storagemodifiertrait.php @@ -0,0 +1,51 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Files\Storage; +use \OCA\Files_External\Lib\StorageConfig; + +/** + * Trait for objects that can modify StorageConfigs and wrap Storages + */ +trait StorageModifierTrait { + + /** + * Modify a StorageConfig parameters + * + * @param StorageConfig $storage + */ + public function manipulateStorageConfig(StorageConfig &$storage) { + } + + /** + * Wrap a Storage if necessary + * + * @param Storage $storage + * @return Storage + */ + public function wrapStorage(Storage $storage) { + return $storage; + } + +} + diff --git a/apps/files_external/lib/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php new file mode 100644 index 0000000000..06c95dd70c --- /dev/null +++ b/apps/files_external/lib/visibilitytrait.php @@ -0,0 +1,129 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement visibility mechanics for a configuration class + */ +trait VisibilityTrait { + + /** @var int visibility */ + protected $visibility = BackendService::VISIBILITY_DEFAULT; + + /** @var int allowed visibilities */ + protected $allowedVisibility = BackendService::VISIBILITY_DEFAULT; + + /** + * @return int + */ + public function getVisibility() { + return $this->visibility; + } + + /** + * Check if the backend is visible for a user type + * + * @param int $visibility + * @return bool + */ + public function isVisibleFor($visibility) { + if ($this->visibility & $visibility) { + return true; + } + return false; + } + + /** + * @param int $visibility + * @return self + */ + public function setVisibility($visibility) { + $this->visibility = $visibility; + $this->allowedVisibility |= $visibility; + return $this; + } + + /** + * @param int $visibility + * @return self + */ + public function addVisibility($visibility) { + return $this->setVisibility($this->visibility | $visibility); + } + + /** + * @param int $visibility + * @return self + */ + public function removeVisibility($visibility) { + return $this->setVisibility($this->visibility & ~$visibility); + } + + /** + * @return int + */ + public function getAllowedVisibility() { + return $this->allowedVisibility; + } + + /** + * Check if the backend is allowed to be visible for a user type + * + * @param int $allowedVisibility + * @return bool + */ + public function isAllowedVisibleFor($allowedVisibility) { + if ($this->allowedVisibility & $allowedVisibility) { + return true; + } + return false; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function setAllowedVisibility($allowedVisibility) { + $this->allowedVisibility = $allowedVisibility; + $this->visibility &= $allowedVisibility; + return $this; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function addAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility | $allowedVisibility); + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function removeAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility & ~$allowedVisibility); + } + +} diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 1ac0f65a1f..e204cdbeb9 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -24,34 +24,20 @@ * */ +use \OCA\Files_External\Service\BackendService; + +$app = new \OCA\Files_external\Appinfo\Application(); +$appContainer = $app->getContainer(); +$backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); +$userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService'); + OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); -$backends = OC_Mount_Config::getPersonalBackends(); - -$mounts = OC_Mount_Config::getPersonalMountPoints(); -$hasId = true; -foreach ($mounts as $mount) { - if (!isset($mount['id'])) { - // some mount points are missing ids - $hasId = false; - break; - } -} - -if (!$hasId) { - $service = new \OCA\Files_external\Service\UserStoragesService(\OC::$server->getUserSession()); - // this will trigger the new storage code which will automatically - // generate storage config ids - $service->getAllStorages(); - // re-read updated config - $mounts = OC_Mount_Config::getPersonalMountPoints(); - // TODO: use the new storage config format in the template -} $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', false); -$tmpl->assign('mounts', $mounts); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); -$tmpl->assign('backends', $backends); +$tmpl->assign('storages', $userStoragesService->getAllStorages()); +$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); +$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL)); return $tmpl->fetchPage(); diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php new file mode 100644 index 0000000000..f5859bc727 --- /dev/null +++ b/apps/files_external/service/backendservice.php @@ -0,0 +1,170 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCP\IConfig; + +use \OCA\Files_External\Lib\Backend\Backend; + +/** + * Service class to manage backend definitions + */ +class BackendService { + + /** Visibility constants for VisibilityTrait */ + const VISIBILITY_NONE = 0; + const VISIBILITY_PERSONAL = 1; + const VISIBILITY_ADMIN = 2; + //const VISIBILITY_ALIENS = 4; + + const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN + + /** Priority constants for PriorityTrait */ + const PRIORITY_DEFAULT = 100; + + /** @var IConfig */ + protected $config; + + /** @var bool */ + private $userMountingAllowed = true; + + /** @var string[] */ + private $userMountingBackends = []; + + /** @var Backend[] */ + private $backends = []; + + /** + * @param IConfig $config + */ + public function __construct( + IConfig $config + ) { + $this->config = $config; + + // Load config values + if ($this->config->getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') { + $this->userMountingAllowed = false; + } + $this->userMountingBackends = explode(',', + $this->config->getAppValue('files_external', 'user_mounting_backends', '') + ); + } + + /** + * Register a backend + * + * @param Backend $backend + */ + public function registerBackend(Backend $backend) { + if (!$this->isAllowedUserBackend($backend)) { + $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL); + } + $this->backends[$backend->getClass()] = $backend; + } + + /** + * @param Backend[] $backends + */ + public function registerBackends(array $backends) { + foreach ($backends as $backend) { + $this->registerBackend($backend); + } + } + + /** + * Get all backends + * + * @return Backend[] + */ + public function getBackends() { + return $this->backends; + } + + /** + * Get all available backends + * + * @return Backend[] + */ + public function getAvailableBackends() { + return array_filter($this->getBackends(), function($backend) { + return empty($backend->checkDependencies()); + }); + } + + /** + * Get backends visible for $visibleFor + * + * @param int $visibleFor + * @return Backend[] + */ + public function getBackendsVisibleFor($visibleFor) { + return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) { + return $backend->isVisibleFor($visibleFor); + }); + } + + /** + * Get backends allowed to be visible for $visibleFor + * + * @param int $visibleFor + * @return Backend[] + */ + public function getBackendsAllowedVisibleFor($visibleFor) { + return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) { + return $backend->isAllowedVisibleFor($visibleFor); + }); + } + + /** + * @param string $class Backend class name + * @return Backend|null + */ + public function getBackend($class) { + if (isset($this->backends[$class])) { + return $this->backends[$class]; + } + return null; + } + + /** + * @return bool + */ + public function isUserMountingAllowed() { + return $this->userMountingAllowed; + } + + /** + * Check a backend if a user is allowed to mount it + * + * @param Backend $backend + * @return bool + */ + protected function isAllowedUserBackend(Backend $backend) { + if ($this->userMountingAllowed && + in_array($backend->getClass(), $this->userMountingBackends) + ) { + return true; + } + return false; + } +} diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 930f994455..5f11d9ace6 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -28,12 +28,23 @@ use \OC\Files\Filesystem; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Service\BackendService; /** * Service class to manage external storages */ abstract class StoragesService { + /** @var BackendService */ + protected $backendService; + + /** + * @param BackendService $backendService + */ + public function __construct(BackendService $backendService) { + $this->backendService = $backendService; + } + /** * Read legacy config data * @@ -60,14 +71,17 @@ abstract class StoragesService { $applicable, $storageOptions ) { - $storageConfig->setBackendClass($storageOptions['class']); + $backend = $this->backendService->getBackend($storageOptions['class']); + $storageConfig->setBackend($backend); + $storageConfig->setBackendOptions($storageOptions['options']); if (isset($storageOptions['mountOptions'])) { $storageConfig->setMountOptions($storageOptions['mountOptions']); } - if (isset($storageOptions['priority'])) { - $storageConfig->setPriority($storageOptions['priority']); + if (!isset($storageOptions['priority'])) { + $storageOptions['priority'] = $backend->getPriority(); } + $storageConfig->setPriority($storageOptions['priority']); if ($mountType === \OC_Mount_Config::MOUNT_TYPE_USER) { $applicableUsers = $storageConfig->getApplicableUsers(); @@ -222,7 +236,7 @@ abstract class StoragesService { $options = [ 'id' => $storageConfig->getId(), - 'class' => $storageConfig->getBackendClass(), + 'class' => $storageConfig->getBackend()->getClass(), 'options' => $storageConfig->getBackendOptions(), ]; @@ -296,6 +310,52 @@ abstract class StoragesService { return $newStorage; } + /** + * Create a storage from its parameters + * + * @param string $mountPoint storage mount point + * @param string $backendClass backend class name + * @param array $backendOptions backend-specific options + * @param array|null $mountOptions mount-specific options + * @param array|null $applicableUsers users for which to mount the storage + * @param array|null $applicableGroups groups for which to mount the storage + * @param int|null $priority priority + * + * @return StorageConfig + */ + public function createStorage( + $mountPoint, + $backendClass, + $backendOptions, + $mountOptions = null, + $applicableUsers = null, + $applicableGroups = null, + $priority = null + ) { + $backend = $this->backendService->getBackend($backendClass); + if (!$backend) { + throw new \InvalidArgumentException('Unable to get backend for backend class '.$backendClass); + } + $newStorage = new StorageConfig(); + $newStorage->setMountPoint($mountPoint); + $newStorage->setBackend($backend); + $newStorage->setBackendOptions($backendOptions); + if (isset($mountOptions)) { + $newStorage->setMountOptions($mountOptions); + } + if (isset($applicableUsers)) { + $newStorage->setApplicableUsers($applicableUsers); + } + if (isset($applicableGroups)) { + $newStorage->setApplicableGroups($applicableGroups); + } + if (isset($priority)) { + $newStorage->setPriority($priority); + } + + return $newStorage; + } + /** * Triggers the given hook signal for all the applicables given * diff --git a/apps/files_external/service/userstoragesservice.php b/apps/files_external/service/userstoragesservice.php index 2f2556043f..7ca911d45f 100644 --- a/apps/files_external/service/userstoragesservice.php +++ b/apps/files_external/service/userstoragesservice.php @@ -26,6 +26,7 @@ use \OC\Files\Filesystem; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Service\BackendService; /** * Service class to manage user external storages @@ -43,12 +44,15 @@ class UserStoragesService extends StoragesService { /** * Create a user storages service * + * @param BackendService $backendService * @param IUserSession $userSession user session */ public function __construct( + BackendService $backendService, IUserSession $userSession ) { $this->userSession = $userSession; + parent::__construct($backendService); } /** diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 6db68713d9..7c53db4c0d 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -26,54 +26,27 @@ * */ +use \OCA\Files_External\Service\BackendService; + OC_Util::checkAdminUser(); +$app = new \OCA\Files_external\Appinfo\Application(); +$appContainer = $app->getContainer(); +$backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); +$globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService'); + OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); -$backends = OC_Mount_Config::getBackends(); -$personal_backends = array(); -$enabled_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); -foreach ($backends as $class => $backend) -{ - if ($class != '\OC\Files\Storage\Local') - { - $personal_backends[$class] = array( - 'backend' => $backend['backend'], - 'enabled' => in_array($class, $enabled_backends), - ); - } -} - -$mounts = OC_Mount_Config::getSystemMountPoints(); -$hasId = true; -foreach ($mounts as $mount) { - if (!isset($mount['id'])) { - // some mount points are missing ids - $hasId = false; - break; - } -} - -if (!$hasId) { - $service = new \OCA\Files_external\Service\GlobalStoragesService(); - // this will trigger the new storage code which will automatically - // generate storage config ids - $service->getAllStorages(); - // re-read updated config - $mounts = OC_Mount_Config::getSystemMountPoints(); - // TODO: use the new storage config format in the template -} - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', true); -$tmpl->assign('mounts', $mounts); -$tmpl->assign('backends', $backends); -$tmpl->assign('personal_backends', $personal_backends); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); -$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes')); +$tmpl->assign('storages', $globalStoragesService->getAllStorages()); +$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN)); +$tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL)); +$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); +$tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); return $tmpl->fetchPage(); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index b886c2e1b1..f931c62eec 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -1,3 +1,8 @@ +

t('External Storage')); ?>

'')) print_unescaped(''.$_['dependencies'].''); ?> @@ -14,103 +19,149 @@ - array('id' => ''))); ?> - - data-id=""> + + - - - - - - - - - - - $value): ?> - + getBackend()->getText()); ?> + + + getBackendOptions(); ?> + getBackend()->getParameters() as $parameter): ?> + getName()])) { + $value = $options[$parameter->getName()]; + } + $placeholder = $parameter->getText(); + $is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL); + + switch ($parameter->getType()) { + case DefinitionParameter::VALUE_PASSWORD: ?> + class="optional" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> - - class="optional" - data-parameter="" - value="" - placeholder="" /> - - - - - - class="optional" - data-parameter="" - value="" - placeholder="" /> - - - - - - - + break; + case DefinitionParameter::VALUE_BOOLEAN: ?> + + + + + class="optional" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> + + + getBackend()->getCustomJs(); + if (isset($customJs)) { + \OCP\Util::addScript('files_external', $customJs); + } + ?> - ' - data-applicable-users=''> - - + + + - + <?php p($l->t('Advanced settings')); ?> - + src="" + /> + - - - + - class="remove" - class="hidden" - ><?php p($l->t('Delete')); ?> + + <?php p($l->t('Delete')); ?> + + + + + + + + + + + + + + + + + + <?php p($l->t('Advanced settings')); ?> + + + + <?php p($l->t('Delete')); ?> + +
@@ -123,9 +174,9 @@

class="hidden"> t('Allow users to mount the following external storage')); ?>
- $backend): ?> - /> -
+ + isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> /> +

diff --git a/apps/files_external/tests/controller/globalstoragescontrollertest.php b/apps/files_external/tests/controller/globalstoragescontrollertest.php index fc58743454..e1bfad8caf 100644 --- a/apps/files_external/tests/controller/globalstoragescontrollertest.php +++ b/apps/files_external/tests/controller/globalstoragescontrollertest.php @@ -28,7 +28,9 @@ use \OCA\Files_external\NotFoundException; class GlobalStoragesControllerTest extends StoragesControllerTest { public function setUp() { parent::setUp(); - $this->service = $this->getMock('\OCA\Files_external\Service\GlobalStoragesService'); + $this->service = $this->getMockBuilder('\OCA\Files_external\Service\GlobalStoragesService') + ->disableOriginalConstructor() + ->getMock(); $this->controller = new GlobalStoragesController( 'files_external', diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index 86874ef978..f3e8c9afba 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -47,10 +47,32 @@ abstract class StoragesControllerTest extends \Test\TestCase { \OC_Mount_Config::$skipTest = false; } + protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getStorageClass') + ->willReturn($storageClass); + $backend->method('getClass') + ->willReturn($storageClass); + return $backend; + } + public function testAddStorage() { + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setBackendOptions([]); + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('addStorage') ->will($this->returnValue($storageConfig)); @@ -71,9 +93,20 @@ abstract class StoragesControllerTest extends \Test\TestCase { } public function testUpdateStorage() { + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setBackendOptions([]); + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('updateStorage') ->will($this->returnValue($storageConfig)); @@ -106,6 +139,14 @@ abstract class StoragesControllerTest extends \Test\TestCase { * @dataProvider mountPointNamesProvider */ public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) { + $storageConfig = new StorageConfig(1); + $storageConfig->setMountPoint($mountPoint); + $storageConfig->setBackend($this->getBackendMock()); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -138,6 +179,9 @@ abstract class StoragesControllerTest extends \Test\TestCase { } public function testAddOrUpdateStorageInvalidBackend() { + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->throwException(new \InvalidArgumentException())); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -170,6 +214,20 @@ abstract class StoragesControllerTest extends \Test\TestCase { } public function testUpdateStorageNonExisting() { + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + + $storageConfig = new StorageConfig(255); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('updateStorage') ->will($this->throwException(new NotFoundException())); @@ -206,9 +264,10 @@ abstract class StoragesControllerTest extends \Test\TestCase { } public function testGetStorage() { + $backend = $this->getBackendMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); - $storageConfig->setBackendClass('\OC\Files\Storage\SMB'); + $storageConfig->setBackend($backend); $storageConfig->setBackendOptions(['user' => 'test', 'password', 'password123']); $storageConfig->setMountOptions(['priority' => false]); diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php index f9b4c5b268..99825f2639 100644 --- a/apps/files_external/tests/controller/userstoragescontrollertest.php +++ b/apps/files_external/tests/controller/userstoragescontrollertest.php @@ -24,6 +24,8 @@ use \OCA\Files_external\Controller\UserStoragesController; use \OCA\Files_external\Service\UserStoragesService; use \OCP\AppFramework\Http; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Service\BackendService; class UserStoragesControllerTest extends StoragesControllerTest { @@ -44,41 +46,22 @@ class UserStoragesControllerTest extends StoragesControllerTest { $this->getMock('\OCP\IL10N'), $this->service ); - - $config = \OC::$server->getConfig(); - - $this->oldAllowedBackends = $config->getAppValue( - 'files_external', - 'user_mounting_backends', - '' - ); - $config->setAppValue( - 'files_external', - 'user_mounting_backends', - '\OC\Files\Storage\SMB' - ); } - public function tearDown() { - $config = \OC::$server->getConfig(); - $config->setAppValue( - 'files_external', - 'user_mounting_backends', - $this->oldAllowedBackends - ); - parent::tearDown(); - } + public function testAddOrUpdateStorageDisallowedBackend() { + $backend = $this->getBackendMock(); + $backend->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->willReturn(false); - function disallowedBackendClassProvider() { - return array( - array('\OC\Files\Storage\Local'), - array('\OC\Files\Storage\FTP'), - ); - } - /** - * @dataProvider disallowedBackendClassProvider - */ - public function testAddOrUpdateStorageDisallowedBackend($backendClass) { + $storageConfig = new StorageConfig(1); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -86,7 +69,7 @@ class UserStoragesControllerTest extends StoragesControllerTest { $response = $this->controller->create( 'mount', - $backendClass, + '\OC\Files\Storage\SMB', array(), [], [], @@ -99,7 +82,7 @@ class UserStoragesControllerTest extends StoragesControllerTest { $response = $this->controller->update( 1, 'mount', - $backendClass, + '\OC\Files\Storage\SMB', array(), [], [], diff --git a/apps/files_external/tests/dynamicmountconfig.php b/apps/files_external/tests/dynamicmountconfig.php deleted file mode 100644 index 48791ca89a..0000000000 --- a/apps/files_external/tests/dynamicmountconfig.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @author Morris Jobke - * @author Thomas Müller - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see - * - */ - -require_once __DIR__ . '/../../../lib/base.php'; - -/** - * Class Test_Mount_Config_Dummy_Backend - */ -class Test_Mount_Config_Dummy_Backend { - public static $checkDependencies = true; - - public static function checkDependencies() { - return self::$checkDependencies; - } -} - -/** - * Class Test_Dynamic_Mount_Config - */ -class Test_Dynamic_Mount_Config extends \Test\TestCase { - - private $backup; - - public function testRegistration() { - - // second registration shall return false - $result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array( - 'backend' => 'Test Dummy', - 'configuration' => array(), - 'has_dependencies' => true)); - - $this->assertTrue($result); - } - - public function testDependencyGetBackend() { - - // is the backend listed? - Test_Mount_Config_Dummy_Backend::$checkDependencies = true; - $backEnds = OC_Mount_Config::getBackends(); - $this->assertArrayHasKey('Test_Mount_Config_Dummy_Backend', $backEnds); - - // backend shall not be listed - Test_Mount_Config_Dummy_Backend::$checkDependencies = false; - - $backEnds = OC_Mount_Config::getBackends(); - $this->assertArrayNotHasKey('Test_Mount_Config_Dummy_Backend', $backEnds); - - } - - public function testCheckDependencies() { - - Test_Mount_Config_Dummy_Backend::$checkDependencies = true; - $message = OC_Mount_Config::checkDependencies(); - $this->assertEmpty($message); - - // backend shall not be listed - Test_Mount_Config_Dummy_Backend::$checkDependencies = array('dummy'); - - $message = OC_Mount_Config::checkDependencies(); - $this->assertEquals('
Note: "dummy" is not installed. Mounting of Test Dummy is not possible. Please ask your system administrator to install it.', - $message); - - } - - protected function setUp() { - parent::setUp(); - - $this->backup = OC_Mount_Config::setUp(); - - // register dummy backend - $result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array( - 'backend' => 'Test Dummy', - 'configuration' => array(), - 'has_dependencies' => true)); - - $this->assertTrue($result); - } - - protected function tearDown() - { - OC_Mount_Config::setUp($this->backup); - parent::tearDown(); - } -} diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index ac25f58b1d..422f3543f3 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -29,7 +29,7 @@ use \OCA\Files_external\Lib\StorageConfig; class GlobalStoragesServiceTest extends StoragesServiceTest { public function setUp() { parent::setUp(); - $this->service = new GlobalStoragesService(); + $this->service = new GlobalStoragesService($this->backendService); } public function tearDown() { @@ -59,7 +59,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { return [ // all users [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', 'backendOptions' => [ @@ -70,11 +70,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { 'applicableUsers' => [], 'applicableGroups' => [], 'priority' => 15, - ]), + ], ], // some users [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', 'backendOptions' => [ @@ -85,11 +85,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { 'applicableUsers' => ['user1', 'user2'], 'applicableGroups' => [], 'priority' => 15, - ]), + ], ], // some groups [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', 'backendOptions' => [ @@ -100,11 +100,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { 'applicableUsers' => [], 'applicableGroups' => ['group1', 'group2'], 'priority' => 15, - ]), + ], ], // both users and groups [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', 'backendOptions' => [ @@ -115,7 +115,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { 'applicableUsers' => ['user1', 'user2'], 'applicableGroups' => ['group1', 'group2'], 'priority' => 15, - ]), + ], ], ]; } @@ -123,7 +123,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { /** * @dataProvider storageDataProvider */ - public function testAddStorage($storage) { + public function testAddStorage($storageParams) { + $storage = $this->makeStorageConfig($storageParams); $newStorage = $this->service->addStorage($storage); $this->assertEquals(1, $newStorage->getId()); @@ -132,7 +133,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $newStorage = $this->service->getStorage(1); $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); - $this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass()); + $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals($storage->getApplicableUsers(), $newStorage->getApplicableUsers()); $this->assertEquals($storage->getApplicableGroups(), $newStorage->getApplicableGroups()); @@ -148,7 +149,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { /** * @dataProvider storageDataProvider */ - public function testUpdateStorage($updatedStorage) { + public function testUpdateStorage($updatedStorageParams) { + $updatedStorage = $this->makeStorageConfig($updatedStorageParams); $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 36f68a83b1..99e179cc93 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -24,6 +24,7 @@ use \OC\Files\Filesystem; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\BackendService; abstract class StoragesServiceTest extends \Test\TestCase { @@ -32,6 +33,9 @@ abstract class StoragesServiceTest extends \Test\TestCase { */ protected $service; + /** @var BackendService */ + protected $backendService; + /** * Data directory * @@ -55,6 +59,25 @@ abstract class StoragesServiceTest extends \Test\TestCase { ); \OC_Mount_Config::$skipTest = true; + $this->backendService = + $this->getMockBuilder('\OCA\Files_External\Service\BackendService') + ->disableOriginalConstructor() + ->getMock(); + + $backends = [ + '\OC\Files\Storage\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'), + '\OC\Files\Storage\SFTP' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'), + ]; + $this->backendService->method('getBackend') + ->will($this->returnCallback(function($backendClass) use ($backends) { + if (isset($backends[$backendClass])) { + return $backends[$backendClass]; + } + return null; + })); + $this->backendService->method('getBackends') + ->will($this->returnValue($backends)); + \OCP\Util::connectHook( Filesystem::CLASSNAME, Filesystem::signal_create_mount, @@ -71,6 +94,17 @@ abstract class StoragesServiceTest extends \Test\TestCase { self::$hookCalls = array(); } + protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getStorageClass') + ->willReturn($storageClass); + $backend->method('getClass') + ->willReturn($storageClass); + return $backend; + } + /** * Creates a StorageConfig instance based on array data * @@ -84,7 +118,12 @@ abstract class StoragesServiceTest extends \Test\TestCase { $storage->setId($data['id']); } $storage->setMountPoint($data['mountPoint']); - $storage->setBackendClass($data['backendClass']); + if (!isset($data['backend'])) { + // data providers are run before $this->backendService is initialised + // so $data['backend'] can be specified directly + $data['backend'] = $this->backendService->getBackend($data['backendClass']); + } + $storage->setBackend($data['backend']); $storage->setBackendOptions($data['backendOptions']); if (isset($data['applicableUsers'])) { $storage->setApplicableUsers($data['applicableUsers']); @@ -106,16 +145,18 @@ abstract class StoragesServiceTest extends \Test\TestCase { * @expectedException \OCA\Files_external\NotFoundException */ public function testNonExistingStorage() { + $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); - $storage->setBackendClass('\OC\Files\Storage\SMB'); + $storage->setBackend($backend); $this->service->updateStorage($storage); } public function testDeleteStorage() { + $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); - $storage->setBackendClass('\OC\Files\Storage\SMB'); + $storage->setBackend($backend); $storage->setBackendOptions(['password' => 'testPassword']); $newStorage = $this->service->addStorage($storage); diff --git a/apps/files_external/tests/service/userstoragesservicetest.php b/apps/files_external/tests/service/userstoragesservicetest.php index ab102741ee..98a9993918 100644 --- a/apps/files_external/tests/service/userstoragesservicetest.php +++ b/apps/files_external/tests/service/userstoragesservicetest.php @@ -40,7 +40,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { ->method('getUser') ->will($this->returnValue($this->user)); - $this->service = new UserStoragesService($userSession); + $this->service = new UserStoragesService($this->backendService, $userSession); // create home folder mkdir($this->dataDir . '/' . $this->userId . '/'); @@ -76,7 +76,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { $newStorage = $this->service->getStorage(1); $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); - $this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass()); + $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals(1, $newStorage->getId()); $this->assertEquals(0, $newStorage->getStatus()); diff --git a/apps/files_external/tests/storageconfigtest.php b/apps/files_external/tests/storageconfigtest.php index c30a4935ce..69edb36e70 100644 --- a/apps/files_external/tests/storageconfigtest.php +++ b/apps/files_external/tests/storageconfigtest.php @@ -26,9 +26,15 @@ use \OCA\Files_external\Lib\StorageConfig; class StorageConfigTest extends \Test\TestCase { public function testJsonSerialization() { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getClass') + ->willReturn('\OC\Files\Storage\SMB'); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); - $storageConfig->setBackendClass('\OC\Files\Storage\SMB'); + $storageConfig->setBackend($backend); $storageConfig->setBackendOptions(['user' => 'test', 'password' => 'password123']); $storageConfig->setPriority(128); $storageConfig->setApplicableUsers(['user1', 'user2']); From a6a69ef1dfe1545e1362953803219ed6f28f71a5 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 14:22:27 +0100 Subject: [PATCH 02/10] Introduce UserGlobalStoragesService UserGlobalStoragesService reads the global storage configuration, cherry-picking storages applicable to a user. Writing storages through this service is forbidden, on punishment of throwing an exception. Storage IDs may also be config hashes when retrieved from this service, as it is unable to update the storages with real IDs. As UserGlobalStoragesService and UserStoragesService share a bit of code relating to users, that has been split into UserTrait. UserTrait also allows for the user set to be overridden, rather than using the user from IUserSession. Config\ConfigAdapter has been reworked to use UserStoragesService and UserGlobalStoragesService instead of OC_Mount_Config::getAbsoluteMountPoints(), further reducing dependance on that horrible static class. --- .../lib/config/configadapter.php | 98 +++++++++++++--- .../service/globalstoragesservice.php | 2 +- .../service/storagesservice.php | 38 +++++-- .../service/userglobalstoragesservice.php | 107 ++++++++++++++++++ .../service/userstoragesservice.php | 29 +++-- apps/files_external/service/usertrait.php | 74 ++++++++++++ 6 files changed, 312 insertions(+), 36 deletions(-) create mode 100644 apps/files_external/service/userglobalstoragesservice.php create mode 100644 apps/files_external/service/usertrait.php diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index 6956de1e74..63615e716a 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -2,6 +2,7 @@ /** * @author Morris Jobke * @author Robin Appelman + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -22,23 +23,67 @@ namespace OCA\Files_External\Config; +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; use OCP\IUser; use OCA\Files_external\Service\UserStoragesService; +use OCA\Files_External\Service\UserGlobalStoragesService; +use OCA\Files_External\Lib\StorageConfig; /** * Make the old files_external config work with the new public mount config api */ class ConfigAdapter implements IMountProvider { + /** @var UserStoragesService */ + private $userStoragesService; + + /** @var UserGlobalStoragesService */ + private $userGlobalStoragesService; + /** * @param UserStoragesService $userStoragesService + * @param UserGlobalStoragesService $userGlobalStoragesService */ - public function __construct(UserStoragesService $userStoragesService) { + public function __construct( + UserStoragesService $userStoragesService, + UserGlobalStoragesService $userGlobalStoragesService + ) { $this->userStoragesService = $userStoragesService; + $this->userGlobalStoragesService = $userGlobalStoragesService; + } + + /** + * Process storage ready for mounting + * + * @param StorageConfig $storage + */ + private function prepareStorageConfig(StorageConfig &$storage) { + $objectStore = $storage->getBackendOption('objectstore'); + if ($objectStore) { + $objectClass = $objectStore['class']; + $storage->setBackendOption('objectstore', new $objectClass($objectStore)); + } + + $storage->getBackend()->manipulateStorageConfig($storage); + } + + /** + * Construct the storage implementation + * + * @param StorageConfig $storageConfig + * @return Storage + */ + private function constructStorage(StorageConfig $storageConfig) { + $class = $storageConfig->getBackend()->getStorageClass(); + $storage = new $class($storageConfig->getBackendOptions()); + + $storage = $storageConfig->getBackend()->wrapStorage($storage); + + return $storage; } /** @@ -49,21 +94,44 @@ class ConfigAdapter implements IMountProvider { * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $loader) { - $mountPoints = \OC_Mount_Config::getAbsoluteMountPoints($user->getUID()); - $mounts = array(); - foreach ($mountPoints as $mountPoint => $options) { - if (isset($options['options']['objectstore'])) { - $objectClass = $options['options']['objectstore']['class']; - $options['options']['objectstore'] = new $objectClass($options['options']['objectstore']); - } - $mountOptions = isset($options['mountOptions']) ? $options['mountOptions'] : []; - if (isset($options['personal']) && $options['personal']) { - $mount = new PersonalMount($this->userStoragesService, $options['id'], $options['class'], $mountPoint, $options['options'], $loader, $mountOptions); - $mounts[] = $mount; - } else { - $mounts[] = new MountPoint($options['class'], $mountPoint, $options['options'], $loader, $mountOptions); - } + $mounts = []; + + $this->userStoragesService->setUser($user); + $this->userGlobalStoragesService->setUser($user); + + foreach ($this->userGlobalStoragesService->getAllStorages() as $storage) { + $this->prepareStorageConfig($storage); + $impl = $this->constructStorage($storage); + + $mount = new MountPoint( + $impl, + '/'.$user->getUID().'/files' . $storage->getMountPoint(), + null, + $loader, + $storage->getMountOptions() + ); + $mounts[$storage->getMountPoint()] = $mount; } + + foreach ($this->userStoragesService->getAllStorages() as $storage) { + $this->prepareStorageConfig($storage); + $impl = $this->constructStorage($storage); + + $mount = new PersonalMount( + $this->userStoragesService, + $storage->getId(), + $impl, + '/'.$user->getUID().'/files' . $storage->getMountPoint(), + null, + $loader, + $storage->getMountOptions() + ); + $mounts[$storage->getMountPoint()] = $mount; + } + + $this->userStoragesService->resetUser(); + $this->userGlobalStoragesService->resetUser(); + return $mounts; } } diff --git a/apps/files_external/service/globalstoragesservice.php b/apps/files_external/service/globalstoragesservice.php index 04445127b3..0e2d3f2b9c 100644 --- a/apps/files_external/service/globalstoragesservice.php +++ b/apps/files_external/service/globalstoragesservice.php @@ -92,7 +92,7 @@ class GlobalStoragesService extends StoragesService { $storageConfig->setBackendOptions($oldBackendOptions); } - \OC_Mount_Config::writeData(null, $mountPoints); + $this->writeLegacyConfig($mountPoints); } /** diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 5f11d9ace6..d3bde0ae96 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -55,6 +55,16 @@ abstract class StoragesService { return \OC_Mount_Config::readData(); } + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + // write global config + \OC_Mount_Config::writeData(null, $mountPoints); + } + /** * Copy legacy storage options into the given storage config object. * @@ -202,20 +212,30 @@ abstract class StoragesService { // process storages with config hash, they must get a real id if (!empty($storagesWithConfigHash)) { - $nextId = $this->generateNextId($storages); - foreach ($storagesWithConfigHash as $storage) { - $storage->setId($nextId); - $storages[$nextId] = $storage; - $nextId++; - } - - // re-save the config with the generated ids - $this->writeConfig($storages); + $this->setRealStorageIds($storages, $storagesWithConfigHash); } return $storages; } + /** + * Replace config hash ID with real IDs, for migrating legacy storages + * + * @param StorageConfig[] $storages Storages with real IDs + * @param StorageConfig[] $storagesWithConfigHash Storages with config hash IDs + */ + protected function setRealStorageIds(array &$storages, array $storagesWithConfigHash) { + $nextId = $this->generateNextId($storages); + foreach ($storagesWithConfigHash as $storage) { + $storage->setId($nextId); + $storages[$nextId] = $storage; + $nextId++; + } + + // re-save the config with the generated ids + $this->writeConfig($storages); + } + /** * Add mount point into the messy mount point structure * diff --git a/apps/files_external/service/userglobalstoragesservice.php b/apps/files_external/service/userglobalstoragesservice.php new file mode 100644 index 0000000000..7852041955 --- /dev/null +++ b/apps/files_external/service/userglobalstoragesservice.php @@ -0,0 +1,107 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCA\Files_external\Service\GlobalStoragesService; +use \OCA\Files_External\Service\BackendService; +use \OCP\IUserSession; +use \OCP\IGroupManager; +use \OCA\Files_External\Service\UserTrait; + +/** + * Service class to read global storages applicable to the user + * Read-only access available, attempting to write will throw DomainException + */ +class UserGlobalStoragesService extends GlobalStoragesService { + + use UserTrait; + + /** @var IGroupManager */ + protected $groupManager; + + /** + * @param BackendService $backendService + * @param IUserSession $userSession + * @param IGroupManager $groupManager + */ + public function __construct( + BackendService $backendService, + IUserSession $userSession, + IGroupManager $groupManager + ) { + parent::__construct($backendService); + $this->userSession = $userSession; + $this->groupManager = $groupManager; + } + + /** + * Replace config hash ID with real IDs, for migrating legacy storages + * + * @param StorageConfig[] $storages Storages with real IDs + * @param StorageConfig[] $storagesWithConfigHash Storages with config hash IDs + */ + protected function setRealStorageIds(array &$storages, array $storagesWithConfigHash) { + // as a read-only view, storage IDs don't need to be real + foreach ($storagesWithConfigHash as $storage) { + $storages[$storage->getId()] = $storage; + } + } + + /** + * Read legacy config data + * + * @return array list of mount configs + */ + protected function readLegacyConfig() { + // read global config + $data = parent::readLegacyConfig(); + $userId = $this->getUser()->getUID(); + + if (isset($data[\OC_Mount_Config::MOUNT_TYPE_USER])) { + $data[\OC_Mount_Config::MOUNT_TYPE_USER] = array_filter( + $data[\OC_Mount_Config::MOUNT_TYPE_USER], function($key) use ($userId) { + return (strtolower($key) === strtolower($userId) || $key === 'all'); + }, ARRAY_FILTER_USE_KEY + ); + } + + if (isset($data[\OC_Mount_Config::MOUNT_TYPE_GROUP])) { + $data[\OC_Mount_Config::MOUNT_TYPE_GROUP] = array_filter( + $data[\OC_Mount_Config::MOUNT_TYPE_GROUP], function($key) use ($userId) { + return ($this->groupManager->isInGroup($userId, $key)); + }, ARRAY_FILTER_USE_KEY + ); + } + + return $data; + } + + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + throw new \DomainException('UserGlobalStoragesService writing disallowed'); + } + +} diff --git a/apps/files_external/service/userstoragesservice.php b/apps/files_external/service/userstoragesservice.php index 7ca911d45f..c69b8d4f51 100644 --- a/apps/files_external/service/userstoragesservice.php +++ b/apps/files_external/service/userstoragesservice.php @@ -27,6 +27,7 @@ use \OC\Files\Filesystem; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_external\NotFoundException; use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Service\UserTrait; /** * Service class to manage user external storages @@ -34,12 +35,7 @@ use \OCA\Files_External\Service\BackendService; */ class UserStoragesService extends StoragesService { - /** - * User session - * - * @var IUserSession - */ - private $userSession; + use UserTrait; /** * Create a user storages service @@ -62,17 +58,28 @@ class UserStoragesService extends StoragesService { */ protected function readLegacyConfig() { // read user config - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); return \OC_Mount_Config::readData($user); } + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + // write user config + $user = $this->getUser()->getUID(); + \OC_Mount_Config::writeData($user, $mountPoints); + } + /** * Read the external storages config * * @return array map of storage id to storage config */ protected function readConfig() { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // TODO: in the future don't rely on the global config reading code $storages = parent::readConfig(); @@ -99,7 +106,7 @@ class UserStoragesService extends StoragesService { * @param array $storages map of storage id to storage config */ public function writeConfig($storages) { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // let the horror begin $mountPoints = []; @@ -127,7 +134,7 @@ class UserStoragesService extends StoragesService { $storageConfig->setBackendOptions($oldBackendOptions); } - \OC_Mount_Config::writeData($user, $mountPoints); + $this->writeLegacyConfig($mountPoints); } /** @@ -138,7 +145,7 @@ class UserStoragesService extends StoragesService { * @param string $signal signal to trigger */ protected function triggerHooks(StorageConfig $storage, $signal) { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // trigger hook for the current user $this->triggerApplicableHooks( diff --git a/apps/files_external/service/usertrait.php b/apps/files_external/service/usertrait.php new file mode 100644 index 0000000000..4f84543565 --- /dev/null +++ b/apps/files_external/service/usertrait.php @@ -0,0 +1,74 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCP\IUserSession; +use \OCP\IUser; + +/** + * Trait for getting user information in a service + */ +trait UserTrait { + + /** @var IUserSession */ + protected $userSession; + + /** + * User override + * + * @var IUser|null + */ + private $user = null; + + /** + * @return IUser|null + */ + protected function getUser() { + if ($this->user) { + return $this->user; + } + return $this->userSession->getUser(); + } + + /** + * Override the user from the session + * Unset with ->resetUser() when finished! + * + * @param IUser + * @return self + */ + public function setUser(IUser $user) { + $this->user = $user; + return $this; + } + + /** + * Reset the user override + * + * @return self + */ + public function resetUser() { + $this->user = null; + return $this; + } +} + From 272a46ebe1a5e195a078dde74f5f2ad941923d9e Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 10:54:03 +0100 Subject: [PATCH 03/10] Authentication mechanisms for external storage backends A backend can now specify generic authentication schemes that it supports, instead of specifying the parameters for its authentication method directly. This allows multiple authentication mechanisms to be implemented for a single scheme, providing altered functionality. This commit introduces the backend framework for this feature, and so at this point the UI will be broken as the frontend does not specify the required information. Terminology: - authentication scheme Parameter interface for the authentication method. A backend supporting the 'password' scheme accepts two parameters, 'user' and 'password'. - authentication mechanism Specific mechanism implementing a scheme. Basic mechanisms may forward configuration options directly to the backend, more advanced ones may lookup parameters or retrieve them from the session New dropdown selector for external storage configurations to select the authentication mechanism to be used. Authentication mechanisms can have visibilities, just like backends. The API was extended too to make it easier to add/remove visibilities. In addition, the concept of 'allowed visibility' has been introduced, so a backend/auth mechanism can force a maximum visibility level (e.g. Local storage type) that cannot be overridden by configuration in the web UI. An authentication mechanism is a fully instantiated implementation. This allows an implementation to have dependencies injected into it, e.g. an \OCP\IDB for database operations. When a StorageConfig is being prepared for mounting, the authentication mechanism implementation has manipulateStorage() called, which inserts the relevant authentication method options into the storage ready for mounting. --- apps/files_external/appinfo/application.php | 14 ++ .../controller/globalstoragescontroller.php | 6 + .../controller/storagescontroller.php | 20 ++- .../controller/userstoragescontroller.php | 6 + apps/files_external/js/settings.js | 107 ++++++++++----- .../files_external/lib/auth/authmechanism.php | 120 +++++++++++++++++ .../files_external/lib/auth/nullmechanism.php | 40 ++++++ apps/files_external/lib/backend/backend.php | 76 +++++++++++ apps/files_external/lib/config.php | 16 ++- .../lib/config/configadapter.php | 3 + apps/files_external/lib/storageconfig.php | 23 ++++ .../lib/storagemodifiertrait.php | 10 ++ apps/files_external/lib/visibilitytrait.php | 7 + apps/files_external/personal.php | 1 + .../files_external/service/backendservice.php | 91 +++++++++++++ .../service/storagesservice.php | 23 ++++ apps/files_external/settings.php | 1 + apps/files_external/templates/settings.php | 127 +++++++++++------- .../controller/storagescontrollertest.php | 38 +++++- .../controller/userstoragescontrollertest.php | 4 + apps/files_external/tests/js/settingsSpec.js | 22 ++- .../service/globalstoragesservicetest.php | 13 ++ .../tests/service/storagesservicetest.php | 44 ++++++ .../tests/service/userstoragesservicetest.php | 6 + .../tests/storageconfigtest.php | 8 ++ 25 files changed, 740 insertions(+), 86 deletions(-) create mode 100644 apps/files_external/lib/auth/authmechanism.php create mode 100644 apps/files_external/lib/auth/nullmechanism.php diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php index b8b1fdaa27..19da1f724b 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -51,6 +51,7 @@ class Application extends App { }); $this->loadBackends(); + $this->loadAuthMechanisms(); } /** @@ -61,4 +62,17 @@ class Application extends App { $service = $container->query('OCA\\Files_External\\Service\\BackendService'); } + /** + * Load authentication mechanisms provided by this app + */ + protected function loadAuthMechanisms() { + $container = $this->getContainer(); + $service = $container->query('OCA\\Files_External\\Service\\BackendService'); + + $service->registerAuthMechanisms([ + // AuthMechanism::SCHEME_NULL mechanism + $container->query('OCA\Files_External\Lib\Auth\NullMechanism'), + ]); + } + } diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index 11f7fd6afa..2c7b6af419 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -64,6 +64,7 @@ class GlobalStoragesController extends StoragesController { * * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechanism class * @param array $backendOptions backend-specific options * @param array $mountOptions mount-specific options * @param array $applicableUsers users for which to mount the storage @@ -75,6 +76,7 @@ class GlobalStoragesController extends StoragesController { public function create( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions, $applicableUsers, @@ -84,6 +86,7 @@ class GlobalStoragesController extends StoragesController { $newStorage = $this->createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions, $applicableUsers, @@ -115,6 +118,7 @@ class GlobalStoragesController extends StoragesController { * @param int $id storage id * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechansim class * @param array $backendOptions backend-specific options * @param array $mountOptions mount-specific options * @param array $applicableUsers users for which to mount the storage @@ -127,6 +131,7 @@ class GlobalStoragesController extends StoragesController { $id, $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions, $applicableUsers, @@ -136,6 +141,7 @@ class GlobalStoragesController extends StoragesController { $storage = $this->createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions, $applicableUsers, diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index c653b51bf8..5f3779dc8b 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -33,6 +33,7 @@ use \OCA\Files_external\Service\StoragesService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; /** * Base class for storages controllers @@ -77,6 +78,7 @@ abstract class StoragesController extends Controller { * * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechanism class name * @param array $backendOptions backend-specific options * @param array|null $mountOptions mount-specific options * @param array|null $applicableUsers users for which to mount the storage @@ -88,6 +90,7 @@ abstract class StoragesController extends Controller { protected function createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions = null, $applicableUsers = null, @@ -98,6 +101,7 @@ abstract class StoragesController extends Controller { return $this->service->createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions, $applicableUsers, @@ -107,7 +111,7 @@ abstract class StoragesController extends Controller { } catch (\InvalidArgumentException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Invalid backend class "%s"', [$backendClass]) + 'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class') ], Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -134,6 +138,8 @@ abstract class StoragesController extends Controller { /** @var Backend */ $backend = $storage->getBackend(); + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); if (!$backend || $backend->checkDependencies()) { // invalid backend return new DataResponse( @@ -154,6 +160,15 @@ abstract class StoragesController extends Controller { Http::STATUS_UNPROCESSABLE_ENTITY ); } + if (!$authMechanism->validateStorage($storage)) { + // unsatisfied parameters + return new DataResponse( + [ + 'message' => (string)$this->l10n->t('Unsatisfied authentication mechanism parameters') + ], + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } return null; } @@ -167,6 +182,9 @@ abstract class StoragesController extends Controller { * @param StorageConfig $storage storage configuration */ protected function updateStorageStatus(StorageConfig &$storage) { + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + $authMechanism->manipulateStorageConfig($storage); /** @var Backend */ $backend = $storage->getBackend(); $backend->manipulateStorageConfig($storage); diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index 5a5bff7ba7..e72b51ff65 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -109,6 +109,7 @@ class UserStoragesController extends StoragesController { * * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechanism class * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -119,12 +120,14 @@ class UserStoragesController extends StoragesController { public function create( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions ) { $newStorage = $this->createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions ); @@ -152,6 +155,7 @@ class UserStoragesController extends StoragesController { * @param int $id storage id * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechanism class * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -163,12 +167,14 @@ class UserStoragesController extends StoragesController { $id, $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions ) { $storage = $this->createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions ); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 287b466454..7240c246ea 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -220,6 +220,13 @@ StorageConfig.prototype = { */ backendClass: null, + /** + * Authentication mechanism class name + * + * @type string + */ + authMechanismClass: null, + /** * Backend-specific configuration * @@ -273,6 +280,7 @@ StorageConfig.prototype = { var data = { mountPoint: this.mountPoint, backendClass: this.backendClass, + authMechanismClass: this.authMechanismClass, backendOptions: this.backendOptions }; if (this.id) { @@ -579,6 +587,13 @@ MountConfigListView.prototype = { */ _allBackends: null, + /** + * List of all supported authentication mechanisms + * + * @type Object. + */ + _allAuthMechanisms: null, + _encryptionEnabled: false, /** @@ -605,6 +620,7 @@ MountConfigListView.prototype = { // read the backend config that was carefully crammed // into the data-configurations attribute of the select this._allBackends = this.$el.find('.selectBackend').data('configurations'); + this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms'); //initialize hidden input field with list of users and groups this.$el.find('tr:not(#addMountPoint)').each(function(i,tr) { @@ -660,6 +676,7 @@ MountConfigListView.prototype = { }); this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this)); + this.$el.on('change', '.selectAuthMechanism', _.bind(this._onSelectAuthMechanism, this)); }, _onChange: function(event) { @@ -694,40 +711,30 @@ MountConfigListView.prototype = { } $tr.addClass(backendClass); $tr.find('.backend').data('class', backendClass); - var configurations = this._allBackends; - var $td = $tr.find('td.configuration'); - $.each(configurations, function(backend, parameters) { - if (backend === backendClass) { - $.each(parameters['configuration'], function(parameter, placeholder) { - var is_optional = false; - if (placeholder.indexOf('&') === 0) { - is_optional = true; - placeholder = placeholder.substring(1); - } - var newElement; - if (placeholder.indexOf('*') === 0) { - var class_string = is_optional ? ' optional' : ''; - newElement = $(''); - } else if (placeholder.indexOf('!') === 0) { - newElement = $(''); - } else if (placeholder.indexOf('#') === 0) { - newElement = $(''); - } else { - var class_string = is_optional ? ' optional' : ''; - newElement = $(''); - } - highlightInput(newElement); - $td.append(newElement); - }); - var priorityEl = $(''); - $tr.append(priorityEl); - if (parameters['custom'] && $el.find('tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length === 1) { - OC.addScript('files_external', parameters['custom']); - } - $td.children().not('[type=hidden]').first().focus(); - return false; + var backendConfiguration = this._allBackends[backendClass]; + + var selectAuthMechanism = $(''); + $.each(this._allAuthMechanisms, function(authClass, authMechanism) { + if (backendConfiguration['authSchemes'][authMechanism['scheme']]) { + selectAuthMechanism.append( + $('') + ); } }); + $tr.find('td.authentication').append(selectAuthMechanism); + + var $td = $tr.find('td.configuration'); + $.each(backendConfiguration['configuration'], _.partial(this.writeParameterInput, $td)); + + selectAuthMechanism.trigger('change'); // generate configuration parameters for auth mechanism + + var priorityEl = $(''); + $tr.append(priorityEl); + if (backendConfiguration['custom'] && $el.find('tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length === 1) { + OC.addScript('files_external', backendConfiguration['custom']); + } + $td.children().not('[type=hidden]').first().focus(); + $tr.find('td').last().attr('class', 'remove'); $tr.find('td.mountOptionsToggle').removeClass('hidden'); $tr.find('td').last().removeAttr('style'); @@ -736,6 +743,41 @@ MountConfigListView.prototype = { addSelect2($tr.find('.applicableUsers'), this._userListLimit); }, + _onSelectAuthMechanism: function(event) { + var $target = $(event.target); + var $tr = $target.closest('tr'); + + var authMechanismClass = $target.val(); + var authMechanism = this._allAuthMechanisms[authMechanismClass]; + var $td = $tr.find('td.configuration'); + $td.find('.auth-param').remove(); + + $.each(authMechanism['configuration'], _.partial( + this.writeParameterInput, $td, _, _, ['auth-param'] + )); + }, + + writeParameterInput: function($td, parameter, placeholder, classes) { + classes = $.isArray(classes) ? classes : []; + classes.push('added'); + if (placeholder.indexOf('&') === 0) { + classes.push('optional'); + placeholder = placeholder.substring(1); + } + var newElement; + if (placeholder.indexOf('*') === 0) { + newElement = $(''); + } else if (placeholder.indexOf('!') === 0) { + newElement = $(''); + } else if (placeholder.indexOf('#') === 0) { + newElement = $(''); + } else { + newElement = $(''); + } + highlightInput(newElement); + $td.append(newElement); + }, + /** * Gets the storage model from the given row * @@ -751,6 +793,7 @@ MountConfigListView.prototype = { var storage = new this._storageConfigClass(storageId); storage.mountPoint = $tr.find('.mountPoint input').val(); storage.backendClass = $tr.find('.backend').data('class'); + storage.authMechanismClass = $tr.find('.selectAuthMechanism').val(); var classOptions = {}; var configuration = $tr.find('.configuration input'); diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php new file mode 100644 index 0000000000..7da57662db --- /dev/null +++ b/apps/files_external/lib/auth/authmechanism.php @@ -0,0 +1,120 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\FrontendDefinitionTrait; +use \OCA\Files_External\Lib\StorageModifierTrait; + +/** + * Authentication mechanism + * + * An authentication mechanism can have services injected during construction, + * such as \OCP\IDB for database operations. This allows an authentication + * mechanism to perform advanced operations based on provided information. + * + * An authenication scheme defines the parameter interface, common to the + * storage implementation, the backend and the authentication mechanism. + * A storage implementation expects parameters according to the authentication + * scheme, which are provided from the authentication mechanism. + * + * This class uses the following traits: + * - VisibilityTrait + * Restrict usage to admin-only/none + * - FrontendDefinitionTrait + * Specify configuration parameters and other definitions + * - StorageModifierTrait + * Object can affect storage mounting + */ +class AuthMechanism implements \JsonSerializable { + + /** Standard authentication schemes */ + const SCHEME_NULL = 'null'; + const SCHEME_PASSWORD = 'password'; + const SCHEME_OAUTH1 = 'oauth1'; + const SCHEME_OAUTH2 = 'oauth2'; + const SCHEME_PUBLICKEY = 'publickey'; + const SCHEME_OPENSTACK = 'openstack'; + + use VisibilityTrait; + use FrontendDefinitionTrait; + use StorageModifierTrait; + + /** @var string */ + protected $scheme; + + /** + * @return string + */ + public function getClass() { + return '\\'.get_class($this); + } + + /** + * Get the authentication scheme implemented + * See self::SCHEME_* constants + * + * @return string + */ + public function getScheme() { + return $this->scheme; + } + + /** + * @param string $scheme + * @return self + */ + public function setScheme($scheme) { + $this->scheme = $scheme; + return $this; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerialize() { + $data = $this->jsonSerializeDefinition(); + $data['scheme'] = $this->getScheme(); + + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorage(StorageConfig $storage) { + // does the backend actually support this scheme + $supportedSchemes = $storage->getBackend()->getAuthSchemes(); + if (!isset($supportedSchemes[$this->getScheme()])) { + return false; + } + + return $this->validateStorageDefinition($storage); + } + +} diff --git a/apps/files_external/lib/auth/nullmechanism.php b/apps/files_external/lib/auth/nullmechanism.php new file mode 100644 index 0000000000..396649d731 --- /dev/null +++ b/apps/files_external/lib/auth/nullmechanism.php @@ -0,0 +1,40 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_external\Lib\StorageConfig; + +/** + * Null authentication mechanism + */ +class NullMechanism extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setScheme(self::SCHEME_NULL) + ->setText($l->t('None')) + ; + } + +} diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php index e7cd27a1d6..634bcb7bfb 100644 --- a/apps/files_external/lib/backend/backend.php +++ b/apps/files_external/lib/backend/backend.php @@ -27,9 +27,31 @@ use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\PriorityTrait; use \OCA\Files_External\Lib\DependencyTrait; use \OCA\Files_External\Lib\StorageModifierTrait; +use \OCA\Files_External\Lib\Auth\AuthMechanism; /** * Storage backend + * + * A backend can have services injected during construction, + * such as \OCP\IDB for database operations. This allows a backend + * to perform advanced operations based on provided information. + * + * An authenication scheme defines the parameter interface, common to the + * storage implementation, the backend and the authentication mechanism. + * A storage implementation expects parameters according to the authentication + * scheme, which are provided from the authentication mechanism. + * + * This class uses the following traits: + * - VisibilityTrait + * Restrict usage to admin-only/none + * - FrontendDefinitionTrait + * Specify configuration parameters and other definitions + * - PriorityTrait + * Allow objects to prioritize over others with the same mountpoint + * - DependencyTrait + * The object requires certain dependencies to be met + * - StorageModifierTrait + * Object can affect storage mounting */ class Backend implements \JsonSerializable { @@ -42,6 +64,12 @@ class Backend implements \JsonSerializable { /** @var string storage class */ private $storageClass; + /** @var array 'scheme' => true, supported authentication schemes */ + private $authSchemes = []; + + /** @var AuthMechanism|callable authentication mechanism fallback */ + private $legacyAuthMechanism; + /** * @return string */ @@ -66,6 +94,53 @@ class Backend implements \JsonSerializable { return $this; } + /** + * @return array + */ + public function getAuthSchemes() { + if (empty($this->authSchemes)) { + return [AuthMechanism::SCHEME_NULL => true]; + } + return $this->authSchemes; + } + + /** + * @param string $scheme + * @return self + */ + public function addAuthScheme($scheme) { + $this->authSchemes[$scheme] = true; + return $this; + } + + /** + * @param array $parameters storage parameters, for dynamic mechanism selection + * @return AuthMechanism + */ + public function getLegacyAuthMechanism(array $parameters = []) { + if (is_callable($this->legacyAuthMechanism)) { + return call_user_func($this->legacyAuthMechanism, $parameters); + } + return $this->legacyAuthMechanism; + } + + /** + * @param AuthMechanism $authMechanism + * @return self + */ + public function setLegacyAuthMechanism(AuthMechanism $authMechanism) { + $this->legacyAuthMechanism = $authMechanism; + return $this; + } + + /** + * @param callable $callback dynamic auth mechanism selection + * @return self + */ + public function setLegacyAuthMechanismCallback(callable $callback) { + $this->legacyAuthMechanism = $callback; + } + /** * Serialize into JSON for client-side JS * @@ -76,6 +151,7 @@ class Backend implements \JsonSerializable { $data['backend'] = $data['name']; // legacy compat $data['priority'] = $this->getPriority(); + $data['authSchemes'] = $this->getAuthSchemes(); return $data; } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 11dec94621..e720677480 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -123,7 +123,9 @@ class OC_Mount_Config { if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } - + if (!isset($options['authMechanism'])) { + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + } // Override if priority greater if ((!isset($mountPoints[$mountPoint])) @@ -149,6 +151,9 @@ class OC_Mount_Config { if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } + if (!isset($options['authMechanism'])) { + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + } // Override if priority greater if ((!isset($mountPoints[$mountPoint])) @@ -175,6 +180,9 @@ class OC_Mount_Config { if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } + if (!isset($options['authMechanism'])) { + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + } // Override if priority greater or if priority type different if ((!isset($mountPoints[$mountPoint])) @@ -204,6 +212,9 @@ class OC_Mount_Config { if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } + if (!isset($options['authMechanism'])) { + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + } // Override if priority greater or if priority type different if ((!isset($mountPoints[$mountPoint])) @@ -227,6 +238,9 @@ class OC_Mount_Config { if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { $options['personal'] = true; $options['options'] = self::decryptPasswords($options['options']); + if (!isset($options['authMechanism'])) { + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + } // Always override previous config $options['priority_type'] = self::MOUNT_TYPE_PERSONAL; diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index 63615e716a..9829629761 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -68,6 +68,7 @@ class ConfigAdapter implements IMountProvider { $storage->setBackendOption('objectstore', new $objectClass($objectStore)); } + $storage->getAuthMechanism()->manipulateStorageConfig($storage); $storage->getBackend()->manipulateStorageConfig($storage); } @@ -81,7 +82,9 @@ class ConfigAdapter implements IMountProvider { $class = $storageConfig->getBackend()->getStorageClass(); $storage = new $class($storageConfig->getBackendOptions()); + // auth mechanism should fire first $storage = $storageConfig->getBackend()->wrapStorage($storage); + $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage); return $storage; } diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php index cf8271ff4e..96ba2f72ae 100644 --- a/apps/files_external/lib/storageconfig.php +++ b/apps/files_external/lib/storageconfig.php @@ -22,6 +22,7 @@ namespace OCA\Files_external\Lib; use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; /** * External storage configuration @@ -42,6 +43,13 @@ class StorageConfig implements \JsonSerializable { */ private $backend; + /** + * Authentication mechanism + * + * @var AuthMechanism + */ + private $authMechanism; + /** * Backend options * @@ -153,6 +161,20 @@ class StorageConfig implements \JsonSerializable { $this->backend= $backend; } + /** + * @return AuthMechanism + */ + public function getAuthMechanism() { + return $this->authMechanism; + } + + /** + * @param AuthMechanism + */ + public function setAuthMechanism(AuthMechanism $authMechanism) { + $this->authMechanism = $authMechanism; + } + /** * Returns the external storage backend-specific options * @@ -301,6 +323,7 @@ class StorageConfig implements \JsonSerializable { } $result['mountPoint'] = $this->mountPoint; $result['backendClass'] = $this->backend->getClass(); + $result['authMechanismClass'] = $this->authMechanism->getClass(); $result['backendOptions'] = $this->backendOptions; if (!is_null($this->priority)) { $result['priority'] = $this->priority; diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php index f78116103d..a11d265e84 100644 --- a/apps/files_external/lib/storagemodifiertrait.php +++ b/apps/files_external/lib/storagemodifiertrait.php @@ -26,6 +26,16 @@ use \OCA\Files_External\Lib\StorageConfig; /** * Trait for objects that can modify StorageConfigs and wrap Storages + * + * When a storage implementation is being prepared for use, the StorageConfig + * is passed through manipulateStorageConfig() to update any parameters as + * necessary. After the storage implementation has been constructed, it is + * passed through wrapStorage(), potentially replacing the implementation with + * a wrapped storage that changes its behaviour. + * + * Certain configuration options need to be set before the implementation is + * constructed, while others are retrieved directly from the storage + * implementation and so need a wrapper to be modified. */ trait StorageModifierTrait { diff --git a/apps/files_external/lib/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php index 06c95dd70c..dfd2d323ca 100644 --- a/apps/files_external/lib/visibilitytrait.php +++ b/apps/files_external/lib/visibilitytrait.php @@ -25,6 +25,13 @@ use \OCA\Files_External\Service\BackendService; /** * Trait to implement visibility mechanics for a configuration class + * + * The standard visibility defines which users/groups can use or see the + * object. The allowed visibility defines the maximum visibility allowed to be + * set on the object. The standard visibility is often set dynamically by + * stored configuration parameters that can be modified by the administrator, + * while the allowed visibility is set directly by the object and cannot be + * modified by the administrator. */ trait VisibilityTrait { diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index e204cdbeb9..fec1c195bb 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -40,4 +40,5 @@ $tmpl->assign('isAdminPage', false); $tmpl->assign('storages', $userStoragesService->getAllStorages()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL)); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); return $tmpl->fetchPage(); diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php index f5859bc727..c1abbcf2b7 100644 --- a/apps/files_external/service/backendservice.php +++ b/apps/files_external/service/backendservice.php @@ -24,6 +24,7 @@ namespace OCA\Files_External\Service; use \OCP\IConfig; use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; /** * Service class to manage backend definitions @@ -53,6 +54,9 @@ class BackendService { /** @var Backend[] */ private $backends = []; + /** @var AuthMechanism[] */ + private $authMechanisms = []; + /** * @param IConfig $config */ @@ -90,6 +94,26 @@ class BackendService { $this->registerBackend($backend); } } + /** + * Register an authentication mechanism + * + * @param AuthMechanism $authMech + */ + public function registerAuthMechanism(AuthMechanism $authMech) { + if (!$this->isAllowedAuthMechanism($authMech)) { + $authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL); + } + $this->authMechanisms[$authMech->getClass()] = $authMech; + } + + /** + * @param AuthMechanism[] $mechanisms + */ + public function registerAuthMechanisms(array $mechanisms) { + foreach ($mechanisms as $mechanism) { + $this->registerAuthMechanism($mechanism); + } + } /** * Get all backends @@ -146,6 +170,63 @@ class BackendService { return null; } + /** + * Get all authentication mechanisms + * + * @return AuthMechanism[] + */ + public function getAuthMechanisms() { + return $this->authMechanisms; + } + + /** + * Get all authentication mechanisms for schemes + * + * @param string[] $schemes + * @return AuthMechanism[] + */ + public function getAuthMechanismsByScheme(array $schemes) { + return array_filter($this->getAuthMechanisms(), function($authMech) use ($schemes) { + return in_array($authMech->getScheme(), $schemes, true); + }); + } + + /** + * Get authentication mechanisms visible for $visibleFor + * + * @param int $visibleFor + * @return AuthMechanism[] + */ + public function getAuthMechanismsVisibleFor($visibleFor) { + return array_filter($this->getAuthMechanisms(), function($authMechanism) use ($visibleFor) { + return $authMechanism->isVisibleFor($visibleFor); + }); + } + + /** + * Get authentication mechanisms allowed to be visible for $visibleFor + * + * @param int $visibleFor + * @return AuthMechanism[] + */ + public function getAuthMechanismsAllowedVisibleFor($visibleFor) { + return array_filter($this->getAuthMechanisms(), function($authMechanism) use ($visibleFor) { + return $authMechanism->isAllowedVisibleFor($visibleFor); + }); + } + + + /** + * @param string $class + * @return AuthMechanism|null + */ + public function getAuthMechanism($class) { + if (isset($this->authMechanisms[$class])) { + return $this->authMechanisms[$class]; + } + return null; + } + /** * @return bool */ @@ -167,4 +248,14 @@ class BackendService { } return false; } + + /** + * Check an authentication mechanism if a user is allowed to use it + * + * @param AuthMechanism $authMechanism + * @return bool + */ + protected function isAllowedAuthMechanism(AuthMechanism $authMechanism) { + return true; // not implemented + } } diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index d3bde0ae96..b8a1824ba2 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -82,8 +82,22 @@ abstract class StoragesService { $storageOptions ) { $backend = $this->backendService->getBackend($storageOptions['class']); + if (!$backend) { + throw new \UnexpectedValueException('Invalid backend class'); + } $storageConfig->setBackend($backend); + if (isset($storageOptions['authMechanism'])) { + $authMechanism = $this->backendService->getAuthMechanism($storageOptions['authMechanism']); + } else { + $authMechanism = $backend->getLegacyAuthMechanism($storageOptions); + $storageOptions['authMechanism'] = 'null'; // to make error handling easier + } + if (!$authMechanism) { + throw new \UnexpectedValueException('Invalid authentication mechanism class'); + } + $storageConfig->setAuthMechanism($authMechanism); + $storageConfig->setBackendOptions($storageOptions['options']); if (isset($storageOptions['mountOptions'])) { $storageConfig->setMountOptions($storageOptions['mountOptions']); @@ -128,6 +142,7 @@ abstract class StoragesService { * - "priority": storage priority * - "backend": backend class name * - "options": backend-specific options + * - "authMechanism": authentication mechanism class name * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc) */ @@ -257,6 +272,7 @@ abstract class StoragesService { $options = [ 'id' => $storageConfig->getId(), 'class' => $storageConfig->getBackend()->getClass(), + 'authMechanism' => $storageConfig->getAuthMechanism()->getClass(), 'options' => $storageConfig->getBackendOptions(), ]; @@ -335,6 +351,7 @@ abstract class StoragesService { * * @param string $mountPoint storage mount point * @param string $backendClass backend class name + * @param string $authMechanismClass authentication mechanism class * @param array $backendOptions backend-specific options * @param array|null $mountOptions mount-specific options * @param array|null $applicableUsers users for which to mount the storage @@ -346,6 +363,7 @@ abstract class StoragesService { public function createStorage( $mountPoint, $backendClass, + $authMechanismClass, $backendOptions, $mountOptions = null, $applicableUsers = null, @@ -356,9 +374,14 @@ abstract class StoragesService { if (!$backend) { throw new \InvalidArgumentException('Unable to get backend for backend class '.$backendClass); } + $authMechanism = $this->backendService->getAuthMechanism($authMechanismClass); + if (!$authMechanism) { + throw new \InvalidArgumentException('Unable to get authentication mechanism for class '.$authMechanismClass); + } $newStorage = new StorageConfig(); $newStorage->setMountPoint($mountPoint); $newStorage->setBackend($backend); + $newStorage->setAuthMechanism($authMechanism); $newStorage->setBackendOptions($backendOptions); if (isset($mountOptions)) { $newStorage->setMountOptions($mountOptions); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 7c53db4c0d..7e20af0c60 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -46,6 +46,7 @@ $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabl $tmpl->assign('isAdminPage', true); $tmpl->assign('storages', $globalStoragesService->getAllStorages()); $tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN)); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); $tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL)); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index f931c62eec..2328ea9565 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -2,6 +2,56 @@ use \OCA\Files_External\Lib\Backend\Backend; use \OCA\Files_External\Lib\DefinitionParameter; use \OCA\Files_External\Service\BackendService; + + function writeParameterInput($parameter, $options, $classes = []) { + $value = ''; + if (isset($options[$parameter->getName()])) { + $value = $options[$parameter->getName()]; + } + $placeholder = $parameter->getText(); + $is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL); + + switch ($parameter->getType()) { + case DefinitionParameter::VALUE_PASSWORD: ?> + + class="" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> + + + + class="" + data-parameter="getName()); ?>" + value="" + /> + + + class="" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> +

t('External Storage')); ?>

@@ -12,6 +62,7 @@ t('Folder name')); ?> t('External storage')); ?> + t('Authentication')); ?> t('Configuration')); ?> '.$l->t('Available for').''); ?>   @@ -31,60 +82,39 @@ getBackend()->getText()); ?> - - getBackendOptions(); ?> - getBackend()->getParameters() as $parameter): ?> + + class="optional" - data-parameter="getName()); ?>" - value="" - placeholder="" - /> - - - - - - class="optional" - data-parameter="getName()); ?>" - value="" - placeholder="" - /> - getBackend()->getAuthSchemes(); + $authMechanisms = array_filter($_['authMechanisms'], function($mech) use ($authSchemes) { + return isset($authSchemes[$mech->getScheme()]); + }); ?> - + + + + + + getBackendOptions(); + foreach ($storage->getBackend()->getParameters() as $parameter) { + writeParameterInput($parameter, $options); + } + foreach ($storage->getAuthMechanism()->getParameters() as $parameter) { + writeParameterInput($parameter, $options, ['auth-param']); + } + $customJs = $storage->getBackend()->getCustomJs(); if (isset($customJs)) { \OCP\Util::addScript('files_external', $customJs); } + $customJsAuth = $storage->getAuthMechanism()->getCustomJs(); + if (isset($customJsAuth)) { + \OCP\Util::addScript('files_external', $customJsAuth); + } ?> @@ -140,7 +170,8 @@ - + '> + diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index f3e8c9afba..735e760c09 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -58,7 +58,22 @@ abstract class StoragesControllerTest extends \Test\TestCase { return $backend; } + protected function getAuthMechMock($scheme = 'null', $class = '\OCA\Files_External\Lib\Auth\NullMechanism') { + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getScheme') + ->willReturn($scheme); + $authMech->method('getClass') + ->willReturn($class); + + return $authMech; + } + public function testAddStorage() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); @@ -68,6 +83,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); $this->service->expects($this->once()) @@ -80,6 +96,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $response = $this->controller->create( 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -88,11 +105,14 @@ abstract class StoragesControllerTest extends \Test\TestCase { ); $data = $response->getData(); - $this->assertEquals($storageConfig, $data); $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + $this->assertEquals($storageConfig, $data); } public function testUpdateStorage() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); @@ -102,6 +122,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); $this->service->expects($this->once()) @@ -115,6 +136,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { 1, 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -123,8 +145,8 @@ abstract class StoragesControllerTest extends \Test\TestCase { ); $data = $response->getData(); - $this->assertEquals($storageConfig, $data); $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + $this->assertEquals($storageConfig, $data); } function mountPointNamesProvider() { @@ -142,6 +164,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint($mountPoint); $storageConfig->setBackend($this->getBackendMock()); + $storageConfig->setAuthMechanism($this->getAuthMechMock()); $storageConfig->setBackendOptions([]); $this->service->expects($this->exactly(2)) @@ -155,6 +178,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $response = $this->controller->create( $mountPoint, '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -168,6 +192,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { 1, $mountPoint, '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -190,6 +215,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $response = $this->controller->create( 'mount', '\OC\Files\Storage\InvalidStorage', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -203,6 +229,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { 1, 'mount', '\OC\Files\Storage\InvalidStorage', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -214,6 +241,9 @@ abstract class StoragesControllerTest extends \Test\TestCase { } public function testUpdateStorageNonExisting() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); @@ -223,6 +253,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { $storageConfig = new StorageConfig(255); $storageConfig->setMountPoint('mount'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); $this->service->expects($this->once()) @@ -236,6 +267,7 @@ abstract class StoragesControllerTest extends \Test\TestCase { 255, 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -265,9 +297,11 @@ abstract class StoragesControllerTest extends \Test\TestCase { public function testGetStorage() { $backend = $this->getBackendMock(); + $authMech = $this->getAuthMechMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions(['user' => 'test', 'password', 'password123']); $storageConfig->setMountOptions(['priority' => false]); diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php index 99825f2639..9f1a8df8d2 100644 --- a/apps/files_external/tests/controller/userstoragescontrollertest.php +++ b/apps/files_external/tests/controller/userstoragescontrollertest.php @@ -53,10 +53,12 @@ class UserStoragesControllerTest extends StoragesControllerTest { $backend->method('isVisibleFor') ->with(BackendService::VISIBILITY_PERSONAL) ->willReturn(false); + $authMech = $this->getAuthMechMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); $this->service->expects($this->exactly(2)) @@ -70,6 +72,7 @@ class UserStoragesControllerTest extends StoragesControllerTest { $response = $this->controller->create( 'mount', '\OC\Files\Storage\SMB', + '\Auth\Mechanism', array(), [], [], @@ -83,6 +86,7 @@ class UserStoragesControllerTest extends StoragesControllerTest { 1, 'mount', '\OC\Files\Storage\SMB', + '\Auth\Mechanism', array(), [], [], diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 7cb86d7270..67a8127712 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -39,6 +39,7 @@ describe('OCA.External.Settings tests', function() { '' + '' + '' + + '' + '' + '' + '' + @@ -58,6 +59,9 @@ describe('OCA.External.Settings tests', function() { 'field1': 'Display Name 1', 'field2': '&Display Name 2' }, + 'authSchemes': { + 'builtin': true, + }, 'priority': 11 }, '\\OC\\AnotherTestBackend': { @@ -66,10 +70,23 @@ describe('OCA.External.Settings tests', function() { 'field1': 'Display Name 1', 'field2': '&Display Name 2' }, + 'authSchemes': { + 'builtin': true, + }, 'priority': 12 } } ); + + $('#externalStorage #addMountPoint .authentication:first').data('mechanisms', { + 'mechanism1': { + 'name': 'Mechanism 1', + 'configuration': { + }, + 'scheme': 'builtin', + }, + }); + }); afterEach(function() { select2Stub.restore(); @@ -80,7 +97,7 @@ describe('OCA.External.Settings tests', function() { var view; function selectBackend(backendName) { - view.$el.find('.selectBackend:first').val('\\OC\\TestBackend').trigger('change'); + view.$el.find('.selectBackend:first').val(backendName).trigger('change'); } beforeEach(function() { @@ -139,7 +156,8 @@ describe('OCA.External.Settings tests', function() { var request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_external/globalstorages'); expect(JSON.parse(request.requestBody)).toEqual({ - backendClass: '\\OC\\TestBackend', + backend: '\\OC\\TestBackend', + authMechanism: 'mechanism1', backendOptions: { 'field1': 'test', 'field2': '' diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index 422f3543f3..d5f99431a5 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -41,6 +41,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -62,6 +63,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -77,6 +79,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -92,6 +95,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -107,6 +111,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -134,6 +139,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); + $this->assertEquals($storage->getAuthMechanism(), $newStorage->getAuthMechanism()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals($storage->getApplicableUsers(), $newStorage->getApplicableUsers()); $this->assertEquals($storage->getApplicableGroups(), $newStorage->getApplicableGroups()); @@ -154,6 +160,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -641,6 +648,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -681,6 +689,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $this->assertEquals(1, $mountPointOptions['id']); $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -698,6 +707,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $this->assertEquals(1, $mountPointOptions['id']); $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -723,12 +733,14 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $legacyConfig = [ 'class' => '\OC\Files\Storage\SMB', + 'authMechanism' => '\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ 'class' => '\OC\Files\Storage\SMB', + 'authMechanism' => '\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; @@ -740,6 +752,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { // different config $legacyConfig3 = [ 'class' => '\OC\Files\Storage\SMB', + 'authMechanism' => '\Auth\Mechanism', 'options' => $legacyBackendOptions2, 'mountOptions' => ['preview' => true], ]; diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 99e179cc93..1429fb1818 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -59,15 +59,39 @@ abstract class StoragesServiceTest extends \Test\TestCase { ); \OC_Mount_Config::$skipTest = true; + // prepare BackendService mock $this->backendService = $this->getMockBuilder('\OCA\Files_External\Service\BackendService') ->disableOriginalConstructor() ->getMock(); + $authMechanisms = [ + '\Auth\Mechanism' => $this->getAuthMechMock('null', '\Auth\Mechanism'), + '\Other\Auth\Mechanism' => $this->getAuthMechMock('null', '\Other\Auth\Mechanism'), + '\OCA\Files_External\Lib\Auth\NullMechanism' => $this->getAuthMechMock(), + ]; + $this->backendService->method('getAuthMechanism') + ->will($this->returnCallback(function($class) use ($authMechanisms) { + if (isset($authMechanisms[$class])) { + return $authMechanisms[$class]; + } + return null; + })); + $this->backendService->method('getAuthMechanismsByScheme') + ->will($this->returnCallback(function($schemes) use ($authMechanisms) { + return array_filter($authMechanisms, function ($authMech) use ($schemes) { + return in_array($authMech->getScheme(), $schemes, true); + }); + })); + $this->backendService->method('getAuthMechanisms') + ->will($this->returnValue($authMechanisms)); + $backends = [ '\OC\Files\Storage\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'), '\OC\Files\Storage\SFTP' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'), ]; + $backends['\OC\Files\Storage\SFTP']->method('getLegacyAuthMechanism') + ->willReturn($authMechanisms['\Other\Auth\Mechanism']); $this->backendService->method('getBackend') ->will($this->returnCallback(function($backendClass) use ($backends) { if (isset($backends[$backendClass])) { @@ -105,6 +129,18 @@ abstract class StoragesServiceTest extends \Test\TestCase { return $backend; } + protected function getAuthMechMock($scheme = 'null', $class = '\OCA\Files_External\Lib\Auth\NullMechanism') { + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getScheme') + ->willReturn($scheme); + $authMech->method('getClass') + ->willReturn($class); + + return $authMech; + } + /** * Creates a StorageConfig instance based on array data * @@ -123,7 +159,11 @@ abstract class StoragesServiceTest extends \Test\TestCase { // so $data['backend'] can be specified directly $data['backend'] = $this->backendService->getBackend($data['backendClass']); } + if (!isset($data['authMechanism'])) { + $data['authMechanism'] = $this->backendService->getAuthMechanism($data['authMechanismClass']); + } $storage->setBackend($data['backend']); + $storage->setAuthMechanism($data['authMechanism']); $storage->setBackendOptions($data['backendOptions']); if (isset($data['applicableUsers'])) { $storage->setApplicableUsers($data['applicableUsers']); @@ -146,17 +186,21 @@ abstract class StoragesServiceTest extends \Test\TestCase { */ public function testNonExistingStorage() { $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); $this->service->updateStorage($storage); } public function testDeleteStorage() { $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); $storage->setBackendOptions(['password' => 'testPassword']); $newStorage = $this->service->addStorage($storage); diff --git a/apps/files_external/tests/service/userstoragesservicetest.php b/apps/files_external/tests/service/userstoragesservicetest.php index 98a9993918..1e57eedd32 100644 --- a/apps/files_external/tests/service/userstoragesservicetest.php +++ b/apps/files_external/tests/service/userstoragesservicetest.php @@ -55,6 +55,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -77,6 +78,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); + $this->assertEquals($storage->getAuthMechanism(), $newStorage->getAuthMechanism()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals(1, $newStorage->getId()); $this->assertEquals(0, $newStorage->getStatus()); @@ -99,6 +101,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', 'backendClass' => '\OC\Files\Storage\SMB', + 'authMechanismClass' => '\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -192,6 +195,7 @@ class UserStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); $backendOptions = $mountPointOptions['options']; @@ -215,12 +219,14 @@ class UserStoragesServiceTest extends StoragesServiceTest { $legacyConfig = [ 'class' => '\OC\Files\Storage\SMB', + 'authMechanism' => '\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ 'class' => '\OC\Files\Storage\SMB', + 'authMechanism' => '\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; diff --git a/apps/files_external/tests/storageconfigtest.php b/apps/files_external/tests/storageconfigtest.php index 69edb36e70..8039990999 100644 --- a/apps/files_external/tests/storageconfigtest.php +++ b/apps/files_external/tests/storageconfigtest.php @@ -32,9 +32,16 @@ class StorageConfigTest extends \Test\TestCase { $backend->method('getClass') ->willReturn('\OC\Files\Storage\SMB'); + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getClass') + ->willReturn('\Auth\Mechanism'); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions(['user' => 'test', 'password' => 'password123']); $storageConfig->setPriority(128); $storageConfig->setApplicableUsers(['user1', 'user2']); @@ -46,6 +53,7 @@ class StorageConfigTest extends \Test\TestCase { $this->assertEquals(1, $json['id']); $this->assertEquals('/test', $json['mountPoint']); $this->assertEquals('\OC\Files\Storage\SMB', $json['backendClass']); + $this->assertEquals('\Auth\Mechanism', $json['authMechanismClass']); $this->assertEquals('test', $json['backendOptions']['user']); $this->assertEquals('password123', $json['backendOptions']['password']); $this->assertEquals(128, $json['priority']); From 1eeca031f863a652d07ebfa2f75339232bf60dc1 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 20:03:11 +0100 Subject: [PATCH 04/10] Split backend identifiers from the class name Prior to this, the storage class name was stored in mount.json under the "class" parameter, and the auth mechanism class name under the "authMechanism" parameter. This decouples the class name from the identifier used to retrieve the backend or auth mechanism. Now, backends/auth mechanisms have a unique identifier, which is saved in the "backend" or "authMechanism" parameter in mount.json respectively. An identifier is considered unique for the object it references, but the underlying class may change (e.g. files_external gets pulled into core and namespaces are modified). --- .../controller/globalstoragescontroller.php | 24 ++--- .../controller/storagescontroller.php | 14 +-- .../controller/userstoragescontroller.php | 26 +++--- apps/files_external/js/settings.js | 32 +++---- .../files_external/lib/auth/authmechanism.php | 9 +- .../files_external/lib/auth/nullmechanism.php | 1 + apps/files_external/lib/backend/backend.php | 10 +-- apps/files_external/lib/config.php | 41 ++++++--- apps/files_external/lib/identifiertrait.php | 68 ++++++++++++++ apps/files_external/lib/storageconfig.php | 4 +- .../files_external/service/backendservice.php | 40 ++++++--- .../service/storagesservice.php | 39 ++++---- apps/files_external/templates/settings.php | 12 +-- .../controller/storagescontrollertest.php | 8 +- .../service/globalstoragesservicetest.php | 89 ++++++++++++++----- .../tests/service/storagesservicetest.php | 50 +++++++---- .../tests/service/userstoragesservicetest.php | 20 ++--- .../tests/storageconfigtest.php | 12 +-- 18 files changed, 327 insertions(+), 172 deletions(-) create mode 100644 apps/files_external/lib/identifiertrait.php diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index 2c7b6af419..756a34fc5d 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -63,8 +63,8 @@ class GlobalStoragesController extends StoragesController { * Create an external storage entry. * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechanism class + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions mount-specific options * @param array $applicableUsers users for which to mount the storage @@ -75,8 +75,8 @@ class GlobalStoragesController extends StoragesController { */ public function create( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, @@ -85,8 +85,8 @@ class GlobalStoragesController extends StoragesController { ) { $newStorage = $this->createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, @@ -117,8 +117,8 @@ class GlobalStoragesController extends StoragesController { * * @param int $id storage id * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechansim class + * @param string $backend backend identifier + * @param string $authMechanism authentication mechansim identifier * @param array $backendOptions backend-specific options * @param array $mountOptions mount-specific options * @param array $applicableUsers users for which to mount the storage @@ -130,8 +130,8 @@ class GlobalStoragesController extends StoragesController { public function update( $id, $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, @@ -140,8 +140,8 @@ class GlobalStoragesController extends StoragesController { ) { $storage = $this->createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 5f3779dc8b..f1d1625bdc 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -77,8 +77,8 @@ abstract class StoragesController extends Controller { * Create a storage from its parameters * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechanism class name + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array|null $mountOptions mount-specific options * @param array|null $applicableUsers users for which to mount the storage @@ -89,8 +89,8 @@ abstract class StoragesController extends Controller { */ protected function createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions = null, $applicableUsers = null, @@ -100,8 +100,8 @@ abstract class StoragesController extends Controller { try { return $this->service->createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, @@ -145,7 +145,7 @@ abstract class StoragesController extends Controller { return new DataResponse( array( 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [ - $storage->getBackend()->getClass() + $storage->getBackend()->getIdentifier() ]) ), Http::STATUS_UNPROCESSABLE_ENTITY diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index e72b51ff65..0d41e088a7 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -83,7 +83,7 @@ class UserStoragesController extends StoragesController { return new DataResponse( array( 'message' => (string)$this->l10n->t('Admin-only storage backend "%s"', [ - $storage->getBackend()->getClass() + $storage->getBackend()->getIdentifier() ]) ), Http::STATUS_UNPROCESSABLE_ENTITY @@ -108,8 +108,8 @@ class UserStoragesController extends StoragesController { * Create an external storage entry. * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechanism class + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -119,15 +119,15 @@ class UserStoragesController extends StoragesController { */ public function create( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ) { $newStorage = $this->createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ); @@ -154,8 +154,8 @@ class UserStoragesController extends StoragesController { * * @param int $id storage id * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechanism class + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -166,15 +166,15 @@ class UserStoragesController extends StoragesController { public function update( $id, $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ) { $storage = $this->createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 7240c246ea..7288f90fa7 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -214,18 +214,18 @@ StorageConfig.prototype = { mountPoint: '', /** - * Backend class name + * Backend * * @type string */ - backendClass: null, + backend: null, /** - * Authentication mechanism class name + * Authentication mechanism * * @type string */ - authMechanismClass: null, + authMechanism: null, /** * Backend-specific configuration @@ -279,8 +279,8 @@ StorageConfig.prototype = { getData: function() { var data = { mountPoint: this.mountPoint, - backendClass: this.backendClass, - authMechanismClass: this.authMechanismClass, + backend: this.backend, + authMechanism: this.authMechanism, backendOptions: this.backendOptions }; if (this.id) { @@ -704,14 +704,14 @@ MountConfigListView.prototype = { $el.find('tbody').append($tr.clone()); $el.find('tbody tr').last().find('.mountPoint input').val(''); var selected = $target.find('option:selected').text(); - var backendClass = $target.val(); + var backend = $target.val(); $tr.find('.backend').text(selected); if ($tr.find('.mountPoint input').val() === '') { $tr.find('.mountPoint input').val(this._suggestMountPoint(selected)); } - $tr.addClass(backendClass); - $tr.find('.backend').data('class', backendClass); - var backendConfiguration = this._allBackends[backendClass]; + $tr.addClass(backend); + $tr.find('.backend').data('class', backend); + var backendConfiguration = this._allBackends[backend]; var selectAuthMechanism = $(''); $.each(this._allAuthMechanisms, function(authClass, authMechanism) { @@ -730,7 +730,7 @@ MountConfigListView.prototype = { var priorityEl = $(''); $tr.append(priorityEl); - if (backendConfiguration['custom'] && $el.find('tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length === 1) { + if (backendConfiguration['custom'] && $el.find('tbody tr.'+backend.replace(/\\/g, '\\\\')).length === 1) { OC.addScript('files_external', backendConfiguration['custom']); } $td.children().not('[type=hidden]').first().focus(); @@ -747,12 +747,12 @@ MountConfigListView.prototype = { var $target = $(event.target); var $tr = $target.closest('tr'); - var authMechanismClass = $target.val(); - var authMechanism = this._allAuthMechanisms[authMechanismClass]; + var authMechanism = $target.val(); + var authMechanismConfiguration = this._allAuthMechanisms[authMechanism]; var $td = $tr.find('td.configuration'); $td.find('.auth-param').remove(); - $.each(authMechanism['configuration'], _.partial( + $.each(authMechanismConfiguration['configuration'], _.partial( this.writeParameterInput, $td, _, _, ['auth-param'] )); }, @@ -792,8 +792,8 @@ MountConfigListView.prototype = { } var storage = new this._storageConfigClass(storageId); storage.mountPoint = $tr.find('.mountPoint input').val(); - storage.backendClass = $tr.find('.backend').data('class'); - storage.authMechanismClass = $tr.find('.selectAuthMechanism').val(); + storage.backend = $tr.find('.backend').data('class'); + storage.authMechanism = $tr.find('.selectAuthMechanism').val(); var classOptions = {}; var configuration = $tr.find('.configuration input'); diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php index 7da57662db..a89ee823d5 100644 --- a/apps/files_external/lib/auth/authmechanism.php +++ b/apps/files_external/lib/auth/authmechanism.php @@ -23,6 +23,7 @@ namespace OCA\Files_External\Lib\Auth; use \OCA\Files_External\Lib\StorageConfig; use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\IdentifierTrait; use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\StorageModifierTrait; @@ -59,17 +60,11 @@ class AuthMechanism implements \JsonSerializable { use VisibilityTrait; use FrontendDefinitionTrait; use StorageModifierTrait; + use IdentifierTrait; /** @var string */ protected $scheme; - /** - * @return string - */ - public function getClass() { - return '\\'.get_class($this); - } - /** * Get the authentication scheme implemented * See self::SCHEME_* constants diff --git a/apps/files_external/lib/auth/nullmechanism.php b/apps/files_external/lib/auth/nullmechanism.php index 396649d731..1765fc6739 100644 --- a/apps/files_external/lib/auth/nullmechanism.php +++ b/apps/files_external/lib/auth/nullmechanism.php @@ -32,6 +32,7 @@ class NullMechanism extends AuthMechanism { public function __construct(IL10N $l) { $this + ->setIdentifier('null::null') ->setScheme(self::SCHEME_NULL) ->setText($l->t('None')) ; diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php index 634bcb7bfb..90d5d38ed9 100644 --- a/apps/files_external/lib/backend/backend.php +++ b/apps/files_external/lib/backend/backend.php @@ -27,6 +27,7 @@ use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\PriorityTrait; use \OCA\Files_External\Lib\DependencyTrait; use \OCA\Files_External\Lib\StorageModifierTrait; +use \OCA\Files_External\Lib\IdentifierTrait; use \OCA\Files_External\Lib\Auth\AuthMechanism; /** @@ -60,6 +61,7 @@ class Backend implements \JsonSerializable { use PriorityTrait; use DependencyTrait; use StorageModifierTrait; + use IdentifierTrait; /** @var string storage class */ private $storageClass; @@ -70,14 +72,6 @@ class Backend implements \JsonSerializable { /** @var AuthMechanism|callable authentication mechanism fallback */ private $legacyAuthMechanism; - /** - * @return string - */ - public function getClass() { - // return storage class for legacy compat - return $this->getStorageClass(); - } - /** * @return string */ diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index e720677480..c0ded522de 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -117,14 +117,17 @@ class OC_Mount_Config { // Global mount points (is this redundant?) if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) { foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) { - $backend = $backendService->getBackend($options['class']); + if (!isset($options['backend'])) { + $options['backend'] = $options['class']; + } + $backend = $backendService->getBackend($options['backend']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); } // Override if priority greater @@ -145,14 +148,17 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } - $backend = $backendService->getBackend($options['class']); + if (!isset($options['backend'])) { + $options['backend'] = $options['class']; + } + $backend = $backendService->getBackend($options['backend']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); } // Override if priority greater @@ -174,14 +180,17 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } - $backend = $backendService->getBackend($options['class']); + if (!isset($options['backend'])) { + $options['backend'] = $options['class']; + } + $backend = $backendService->getBackend($options['backend']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); } // Override if priority greater or if priority type different @@ -206,14 +215,17 @@ class OC_Mount_Config { foreach ($options as &$option) { $option = self::setUserVars($user, $option); } - $backend = $backendService->getBackend($options['class']); + if (!isset($options['backend'])) { + $options['backend'] = $options['class']; + } + $backend = $backendService->getBackend($options['backend']); $options['personal'] = false; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['priority'])) { $options['priority'] = $backend->getPriority(); } if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); } // Override if priority greater or if priority type different @@ -234,12 +246,15 @@ class OC_Mount_Config { $mountConfig = self::readData($user); if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) { foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) { - $backend = $backendService->getBackend($options['class']); + if (!isset($options['backend'])) { + $options['backend'] = $options['class']; + } + $backend = $backendService->getBackend($options['backend']); if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { $options['personal'] = true; $options['options'] = self::decryptPasswords($options['options']); if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass(); + $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); } // Always override previous config @@ -809,7 +824,8 @@ class OC_Mount_Config { public static function makeConfigHash($config) { $data = json_encode( array( - 'c' => $config['class'], + 'c' => $config['backend'], + 'a' => $config['authMechanism'], 'm' => $config['mountpoint'], 'o' => $config['options'], 'p' => isset($config['priority']) ? $config['priority'] : -1, @@ -858,7 +874,8 @@ class OC_Mount_Config { return false; } - $class = $options['class']; + $service = self::$appContainer->query('OCA\Files_External\Service\BackendService'); + $class = $service->getBackend($options['backend'])->getStorageClass(); try { /** @var \OC\Files\Storage\Storage $storage */ $storage = new $class($options['options']); diff --git a/apps/files_external/lib/identifiertrait.php b/apps/files_external/lib/identifiertrait.php new file mode 100644 index 0000000000..139911580f --- /dev/null +++ b/apps/files_external/lib/identifiertrait.php @@ -0,0 +1,68 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * Trait for objects requiring an identifier (and/or identifier aliases) + */ +trait IdentifierTrait { + + /** @var string */ + protected $identifier; + + /** @var string[] */ + protected $identifierAliases = []; + + /** + * @return string + */ + public function getIdentifier() { + return $this->identifier; + } + + /** + * @param string $identifier + * @return self + */ + public function setIdentifier($identifier) { + $this->identifier = $identifier; + $this->identifierAliases[] = $identifier; + return $this; + } + + /** + * @return string[] + */ + public function getIdentifierAliases() { + return $this->identifierAliases; + } + + /** + * @param string $alias + * @return self + */ + public function addIdentifierAlias($alias) { + $this->identifierAliases[] = $alias; + return $this; + } + +} diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php index 96ba2f72ae..aeb8f52707 100644 --- a/apps/files_external/lib/storageconfig.php +++ b/apps/files_external/lib/storageconfig.php @@ -322,8 +322,8 @@ class StorageConfig implements \JsonSerializable { $result['id'] = $this->id; } $result['mountPoint'] = $this->mountPoint; - $result['backendClass'] = $this->backend->getClass(); - $result['authMechanismClass'] = $this->authMechanism->getClass(); + $result['backend'] = $this->backend->getIdentifier(); + $result['authMechanism'] = $this->authMechanism->getIdentifier(); $result['backendOptions'] = $this->backendOptions; if (!is_null($this->priority)) { $result['priority'] = $this->priority; diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php index c1abbcf2b7..bee08ecbd1 100644 --- a/apps/files_external/service/backendservice.php +++ b/apps/files_external/service/backendservice.php @@ -83,7 +83,9 @@ class BackendService { if (!$this->isAllowedUserBackend($backend)) { $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL); } - $this->backends[$backend->getClass()] = $backend; + foreach ($backend->getIdentifierAliases() as $alias) { + $this->backends[$alias] = $backend; + } } /** @@ -103,7 +105,9 @@ class BackendService { if (!$this->isAllowedAuthMechanism($authMech)) { $authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL); } - $this->authMechanisms[$authMech->getClass()] = $authMech; + foreach ($authMech->getIdentifierAliases() as $alias) { + $this->authMechanisms[$alias] = $authMech; + } } /** @@ -121,7 +125,12 @@ class BackendService { * @return Backend[] */ public function getBackends() { - return $this->backends; + // only return real identifiers, no aliases + $backends = []; + foreach ($this->backends as $backend) { + $backends[$backend->getIdentifier()] = $backend; + } + return $backends; } /** @@ -160,12 +169,12 @@ class BackendService { } /** - * @param string $class Backend class name + * @param string $identifier * @return Backend|null */ - public function getBackend($class) { - if (isset($this->backends[$class])) { - return $this->backends[$class]; + public function getBackend($identifier) { + if (isset($this->backends[$identifier])) { + return $this->backends[$identifier]; } return null; } @@ -176,7 +185,12 @@ class BackendService { * @return AuthMechanism[] */ public function getAuthMechanisms() { - return $this->authMechanisms; + // only return real identifiers, no aliases + $mechanisms = []; + foreach ($this->authMechanisms as $mechanism) { + $mechanisms[$mechanism->getIdentifier()] = $mechanism; + } + return $mechanisms; } /** @@ -217,12 +231,12 @@ class BackendService { /** - * @param string $class + * @param string $identifier * @return AuthMechanism|null */ - public function getAuthMechanism($class) { - if (isset($this->authMechanisms[$class])) { - return $this->authMechanisms[$class]; + public function getAuthMechanism($identifier) { + if (isset($this->authMechanisms[$identifier])) { + return $this->authMechanisms[$identifier]; } return null; } @@ -242,7 +256,7 @@ class BackendService { */ protected function isAllowedUserBackend(Backend $backend) { if ($this->userMountingAllowed && - in_array($backend->getClass(), $this->userMountingBackends) + !empty(array_intersect($backend->getIdentifierAliases(), $this->userMountingBackends)) ) { return true; } diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index b8a1824ba2..e89af6bc75 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -81,9 +81,9 @@ abstract class StoragesService { $applicable, $storageOptions ) { - $backend = $this->backendService->getBackend($storageOptions['class']); + $backend = $this->backendService->getBackend($storageOptions['backend']); if (!$backend) { - throw new \UnexpectedValueException('Invalid backend class'); + throw new \UnexpectedValueException('Invalid backend '.$storageOptions['backend']); } $storageConfig->setBackend($backend); @@ -94,7 +94,7 @@ abstract class StoragesService { $storageOptions['authMechanism'] = 'null'; // to make error handling easier } if (!$authMechanism) { - throw new \UnexpectedValueException('Invalid authentication mechanism class'); + throw new \UnexpectedValueException('Invalid authentication mechanism '.$storageOptions['authMechanism']); } $storageConfig->setAuthMechanism($authMechanism); @@ -140,9 +140,10 @@ abstract class StoragesService { * - $mountPath is the mount point path (where the storage must be mounted) * - $storageOptions is a map of storage options: * - "priority": storage priority - * - "backend": backend class name + * - "backend": backend identifier + * - "class": LEGACY backend class name * - "options": backend-specific options - * - "authMechanism": authentication mechanism class name + * - "authMechanism": authentication mechanism identifier * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc) */ @@ -186,6 +187,13 @@ abstract class StoragesService { // options might be needed for the config hash $storageOptions['options'] = \OC_Mount_Config::decryptPasswords($storageOptions['options']); + if (!isset($storageOptions['backend'])) { + $storageOptions['backend'] = $storageOptions['class']; // legacy compat + } + if (!isset($storageOptions['authMechanism'])) { + $storageOptions['authMechanism'] = null; // ensure config hash works + } + if (isset($storageOptions['id'])) { $configId = (int)$storageOptions['id']; if (isset($storages[$configId])) { @@ -271,8 +279,9 @@ abstract class StoragesService { $options = [ 'id' => $storageConfig->getId(), - 'class' => $storageConfig->getBackend()->getClass(), - 'authMechanism' => $storageConfig->getAuthMechanism()->getClass(), + 'backend' => $storageConfig->getBackend()->getIdentifier(), + //'class' => $storageConfig->getBackend()->getClass(), + 'authMechanism' => $storageConfig->getAuthMechanism()->getIdentifier(), 'options' => $storageConfig->getBackendOptions(), ]; @@ -350,8 +359,8 @@ abstract class StoragesService { * Create a storage from its parameters * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name - * @param string $authMechanismClass authentication mechanism class + * @param string $backendIdentifier backend identifier + * @param string $authMechanismIdentifier authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array|null $mountOptions mount-specific options * @param array|null $applicableUsers users for which to mount the storage @@ -362,21 +371,21 @@ abstract class StoragesService { */ public function createStorage( $mountPoint, - $backendClass, - $authMechanismClass, + $backendIdentifier, + $authMechanismIdentifier, $backendOptions, $mountOptions = null, $applicableUsers = null, $applicableGroups = null, $priority = null ) { - $backend = $this->backendService->getBackend($backendClass); + $backend = $this->backendService->getBackend($backendIdentifier); if (!$backend) { - throw new \InvalidArgumentException('Unable to get backend for backend class '.$backendClass); + throw new \InvalidArgumentException('Unable to get backend for '.$backendIdentifier); } - $authMechanism = $this->backendService->getAuthMechanism($authMechanismClass); + $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier); if (!$authMechanism) { - throw new \InvalidArgumentException('Unable to get authentication mechanism for class '.$authMechanismClass); + throw new \InvalidArgumentException('Unable to get authentication mechanism for '.$authMechanismIdentifier); } $newStorage = new StorageConfig(); $newStorage->setMountPoint($mountPoint); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 2328ea9565..589574bbae 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -71,7 +71,7 @@ - + @@ -80,7 +80,7 @@ data-mountpoint="getMountPoint(), '/')); ?>" placeholder="t('Folder name')); ?>" /> - getBackend()->getText()); ?> + getBackend()->getText()); ?> @@ -166,7 +166,7 @@ }); ?> - + @@ -206,7 +206,7 @@

class="hidden"> t('Allow users to mount the following external storage')); ?>
- isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> /> + isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index 735e760c09..5a9683306c 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -53,8 +53,8 @@ abstract class StoragesControllerTest extends \Test\TestCase { ->getMock(); $backend->method('getStorageClass') ->willReturn($storageClass); - $backend->method('getClass') - ->willReturn($storageClass); + $backend->method('getIdentifier') + ->willReturn('identifier:'.$class); return $backend; } @@ -64,8 +64,8 @@ abstract class StoragesControllerTest extends \Test\TestCase { ->getMock(); $authMech->method('getScheme') ->willReturn($scheme); - $authMech->method('getClass') - ->willReturn($class); + $authMech->method('getIdentifier') + ->willReturn('identifier:'.$class); return $authMech; } diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index d5f99431a5..f6ed9f1422 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -40,8 +40,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { protected function makeTestStorageData() { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -62,8 +62,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -78,8 +78,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -94,8 +94,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -110,8 +110,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { [ [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -159,8 +159,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $updatedStorage = $this->makeStorageConfig($updatedStorageParams); $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -647,8 +647,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); - $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -688,8 +688,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); - $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -706,8 +706,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); - $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -732,15 +732,15 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', - 'authMechanism' => '\Auth\Mechanism', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', - 'authMechanism' => '\Auth\Mechanism', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; @@ -751,8 +751,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { // different config $legacyConfig3 = [ - 'class' => '\OC\Files\Storage\SMB', - 'authMechanism' => '\Auth\Mechanism', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions2, 'mountOptions' => ['preview' => true], ]; @@ -826,4 +826,45 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $this->assertEquals([], $storage4->getApplicableGroups()); $this->assertEquals(['preview' => true], $storage4->getMountOptions()); } + + public function testReadLegacyConfigClass() { + $configFile = $this->dataDir . '/mount.json'; + + $json = [ + 'user' => [ + 'user1' => [ + '/$user/files/somemount' => [ + 'class' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + '/$user/files/othermount' => [ + 'class' => 'identifier:sftp_alias', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + ] + ] + ]; + + file_put_contents($configFile, json_encode($json)); + + $allStorages = $this->service->getAllStorages(); + + $this->assertCount(2, $allStorages); + + $storage1 = $allStorages[1]; + $storage2 = $allStorages[2]; + + $this->assertEquals('/somemount', $storage1->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage1->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage1->getAuthMechanism()->getIdentifier()); + + $this->assertEquals('/othermount', $storage2->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage2->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage2->getAuthMechanism()->getIdentifier()); + } + } diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 1429fb1818..397f2a2e5c 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -66,9 +66,9 @@ abstract class StoragesServiceTest extends \Test\TestCase { ->getMock(); $authMechanisms = [ - '\Auth\Mechanism' => $this->getAuthMechMock('null', '\Auth\Mechanism'), - '\Other\Auth\Mechanism' => $this->getAuthMechMock('null', '\Other\Auth\Mechanism'), - '\OCA\Files_External\Lib\Auth\NullMechanism' => $this->getAuthMechMock(), + 'identifier:\Auth\Mechanism' => $this->getAuthMechMock('null', '\Auth\Mechanism'), + 'identifier:\Other\Auth\Mechanism' => $this->getAuthMechMock('null', '\Other\Auth\Mechanism'), + 'identifier:\OCA\Files_External\Lib\Auth\NullMechanism' => $this->getAuthMechMock(), ]; $this->backendService->method('getAuthMechanism') ->will($this->returnCallback(function($class) use ($authMechanisms) { @@ -86,12 +86,14 @@ abstract class StoragesServiceTest extends \Test\TestCase { $this->backendService->method('getAuthMechanisms') ->will($this->returnValue($authMechanisms)); + $sftpBackend = $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'); $backends = [ - '\OC\Files\Storage\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'), - '\OC\Files\Storage\SFTP' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'), + 'identifier:\OCA\Files_External\Lib\Backend\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'), + 'identifier:\OCA\Files_External\Lib\Backend\SFTP' => $sftpBackend, + 'identifier:sftp_alias' => $sftpBackend, ]; - $backends['\OC\Files\Storage\SFTP']->method('getLegacyAuthMechanism') - ->willReturn($authMechanisms['\Other\Auth\Mechanism']); + $backends['identifier:\OCA\Files_External\Lib\Backend\SFTP']->method('getLegacyAuthMechanism') + ->willReturn($authMechanisms['identifier:\Other\Auth\Mechanism']); $this->backendService->method('getBackend') ->will($this->returnCallback(function($backendClass) use ($backends) { if (isset($backends[$backendClass])) { @@ -111,6 +113,14 @@ abstract class StoragesServiceTest extends \Test\TestCase { Filesystem::signal_delete_mount, get_class($this), 'deleteHookCallback'); + $containerMock = $this->getMock('\OCP\AppFramework\IAppContainer'); + $containerMock->method('query') + ->will($this->returnCallback(function($name) { + if ($name === 'OCA\Files_External\Service\BackendService') { + return $this->backendService; + } + })); + \OC_Mount_Config::initApp($containerMock); } public function tearDown() { @@ -124,8 +134,8 @@ abstract class StoragesServiceTest extends \Test\TestCase { ->getMock(); $backend->method('getStorageClass') ->willReturn($storageClass); - $backend->method('getClass') - ->willReturn($storageClass); + $backend->method('getIdentifier') + ->willReturn('identifier:'.$class); return $backend; } @@ -135,8 +145,8 @@ abstract class StoragesServiceTest extends \Test\TestCase { ->getMock(); $authMech->method('getScheme') ->willReturn($scheme); - $authMech->method('getClass') - ->willReturn($class); + $authMech->method('getIdentifier') + ->willReturn('identifier:'.$class); return $authMech; } @@ -157,10 +167,16 @@ abstract class StoragesServiceTest extends \Test\TestCase { if (!isset($data['backend'])) { // data providers are run before $this->backendService is initialised // so $data['backend'] can be specified directly - $data['backend'] = $this->backendService->getBackend($data['backendClass']); + $data['backend'] = $this->backendService->getBackend($data['backendIdentifier']); + } + if (!isset($data['backend'])) { + throw new \Exception('oops, no backend'); } if (!isset($data['authMechanism'])) { - $data['authMechanism'] = $this->backendService->getAuthMechanism($data['authMechanismClass']); + $data['authMechanism'] = $this->backendService->getAuthMechanism($data['authMechanismIdentifier']); + } + if (!isset($data['authMechanism'])) { + throw new \Exception('oops, no auth mechanism'); } $storage->setBackend($data['backend']); $storage->setAuthMechanism($data['authMechanism']); @@ -185,8 +201,8 @@ abstract class StoragesServiceTest extends \Test\TestCase { * @expectedException \OCA\Files_external\NotFoundException */ public function testNonExistingStorage() { - $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); - $authMechanism = $this->backendService->getAuthMechanism('\Auth\Mechanism'); + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); $storage->setBackend($backend); @@ -195,8 +211,8 @@ abstract class StoragesServiceTest extends \Test\TestCase { } public function testDeleteStorage() { - $backend = $this->backendService->getBackend('\OC\Files\Storage\SMB'); - $authMechanism = $this->backendService->getAuthMechanism('\Auth\Mechanism'); + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); $storage->setBackend($backend); diff --git a/apps/files_external/tests/service/userstoragesservicetest.php b/apps/files_external/tests/service/userstoragesservicetest.php index 1e57eedd32..0d5b82e2f8 100644 --- a/apps/files_external/tests/service/userstoragesservicetest.php +++ b/apps/files_external/tests/service/userstoragesservicetest.php @@ -54,8 +54,8 @@ class UserStoragesServiceTest extends StoragesServiceTest { private function makeTestStorageData() { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -100,8 +100,8 @@ class UserStoragesServiceTest extends StoragesServiceTest { public function testUpdateStorage() { $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', - 'authMechanismClass' => '\Auth\Mechanism', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -194,8 +194,8 @@ class UserStoragesServiceTest extends StoragesServiceTest { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); - $this->assertEquals('\Auth\Mechanism', $mountPointOptions['authMechanism']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); $backendOptions = $mountPointOptions['options']; @@ -218,15 +218,15 @@ class UserStoragesServiceTest extends StoragesServiceTest { $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', - 'authMechanism' => '\Auth\Mechanism', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', - 'authMechanism' => '\Auth\Mechanism', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; diff --git a/apps/files_external/tests/storageconfigtest.php b/apps/files_external/tests/storageconfigtest.php index 8039990999..dba5105d7d 100644 --- a/apps/files_external/tests/storageconfigtest.php +++ b/apps/files_external/tests/storageconfigtest.php @@ -29,14 +29,14 @@ class StorageConfigTest extends \Test\TestCase { $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') ->disableOriginalConstructor() ->getMock(); - $backend->method('getClass') - ->willReturn('\OC\Files\Storage\SMB'); + $backend->method('getIdentifier') + ->willReturn('storage::identifier'); $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') ->disableOriginalConstructor() ->getMock(); - $authMech->method('getClass') - ->willReturn('\Auth\Mechanism'); + $authMech->method('getIdentifier') + ->willReturn('auth::identifier'); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); @@ -52,8 +52,8 @@ class StorageConfigTest extends \Test\TestCase { $this->assertEquals(1, $json['id']); $this->assertEquals('/test', $json['mountPoint']); - $this->assertEquals('\OC\Files\Storage\SMB', $json['backendClass']); - $this->assertEquals('\Auth\Mechanism', $json['authMechanismClass']); + $this->assertEquals('storage::identifier', $json['backend']); + $this->assertEquals('auth::identifier', $json['authMechanism']); $this->assertEquals('test', $json['backendOptions']['user']); $this->assertEquals('password123', $json['backendOptions']['password']); $this->assertEquals(128, $json['priority']); From c572631087e2b56aa48c87ac753447e709248234 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 10:00:37 +0100 Subject: [PATCH 05/10] Unit tests for new backend API --- .../tests/auth/authmechanismtest.php | 80 +++++++ .../tests/backend/backendtest.php | 89 ++++++++ .../tests/backend/legacybackendtest.php | 81 +++++++ .../controller/storagescontrollertest.php | 63 +++++ .../tests/definitionparameterttest.php | 70 ++++++ .../tests/dependencytraittest.php | 45 ++++ .../tests/frontenddefinitiontraittest.php | 83 +++++++ .../tests/service/backendservicetest.php | 152 +++++++++++++ .../service/globalstoragesservicetest.php | 40 ++++ .../tests/service/storagesservicetest.php | 59 +++++ .../service/userglobalstoragesservicetest.php | 215 ++++++++++++++++++ 11 files changed, 977 insertions(+) create mode 100644 apps/files_external/tests/auth/authmechanismtest.php create mode 100644 apps/files_external/tests/backend/backendtest.php create mode 100644 apps/files_external/tests/backend/legacybackendtest.php create mode 100644 apps/files_external/tests/definitionparameterttest.php create mode 100644 apps/files_external/tests/dependencytraittest.php create mode 100644 apps/files_external/tests/frontenddefinitiontraittest.php create mode 100644 apps/files_external/tests/service/backendservicetest.php create mode 100644 apps/files_external/tests/service/userglobalstoragesservicetest.php diff --git a/apps/files_external/tests/auth/authmechanismtest.php b/apps/files_external/tests/auth/authmechanismtest.php new file mode 100644 index 0000000000..b09d65a02d --- /dev/null +++ b/apps/files_external/tests/auth/authmechanismtest.php @@ -0,0 +1,80 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Auth; + +class AuthMechanismTest extends \Test\TestCase { + + public function testJsonSerialization() { + $mechanism = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->setMethods(['jsonSerializeDefinition']) + ->getMock(); + $mechanism->expects($this->once()) + ->method('jsonSerializeDefinition') + ->willReturn(['foo' => 'bar']); + + $mechanism->setScheme('scheme'); + + $json = $mechanism->jsonSerialize(); + $this->assertEquals('bar', $json['foo']); + $this->assertEquals('scheme', $json['scheme']); + } + + public function validateStorageProvider() { + return [ + [true, 'scheme', true], + [false, 'scheme', false], + [true, 'foobar', true], + [false, 'barfoo', true], + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $scheme, $definitionSuccess) { + $mechanism = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->setMethods(['validateStorageDefinition']) + ->getMock(); + $mechanism->expects($this->atMost(1)) + ->method('validateStorageDefinition') + ->willReturn($definitionSuccess); + + $mechanism->setScheme($scheme); + + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->once()) + ->method('getAuthSchemes') + ->willReturn(['scheme' => true, 'foobar' => true]); + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + $storageConfig->expects($this->once()) + ->method('getBackend') + ->willReturn($backend); + + $this->assertEquals($expectedSuccess, $mechanism->validateStorage($storageConfig)); + } + +} diff --git a/apps/files_external/tests/backend/backendtest.php b/apps/files_external/tests/backend/backendtest.php new file mode 100644 index 0000000000..4956a923e9 --- /dev/null +++ b/apps/files_external/tests/backend/backendtest.php @@ -0,0 +1,89 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Backend; + +use \OCA\Files_External\Lib\Backend\Backend; + +class BackendTest extends \Test\TestCase { + + public function testJsonSerialization() { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->setMethods(['jsonSerializeDefinition']) + ->getMock(); + $backend->expects($this->once()) + ->method('jsonSerializeDefinition') + ->willReturn(['foo' => 'bar', 'name' => 'abc']); + + $backend->setPriority(57); + $backend->addAuthScheme('foopass'); + $backend->addAuthScheme('barauth'); + + $json = $backend->jsonSerialize(); + $this->assertEquals('bar', $json['foo']); + $this->assertEquals('abc', $json['name']); + $this->assertEquals($json['name'], $json['backend']); + $this->assertEquals(57, $json['priority']); + + $this->assertContains('foopass', $json['authSchemes']); + $this->assertContains('barauth', $json['authSchemes']); + } + + public function validateStorageProvider() { + return [ + [true, true], + [false, false], + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $definitionSuccess) { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->setMethods(['validateStorageDefinition']) + ->getMock(); + $backend->expects($this->atMost(1)) + ->method('validateStorageDefinition') + ->willReturn($definitionSuccess); + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + + $this->assertEquals($expectedSuccess, $backend->validateStorage($storageConfig)); + } + + public function testLegacyAuthMechanismCallback() { + $backend = new Backend(); + $backend->setLegacyAuthMechanismCallback(function(array $params) { + if (isset($params['ping'])) { + return 'pong'; + } + return 'foobar'; + }); + + $this->assertEquals('pong', $backend->getLegacyAuthMechanism(['ping' => true])); + $this->assertEquals('foobar', $backend->getLegacyAuthMechanism(['other' => true])); + $this->assertEquals('foobar', $backend->getLegacyAuthMechanism()); + } + +} diff --git a/apps/files_external/tests/backend/legacybackendtest.php b/apps/files_external/tests/backend/legacybackendtest.php new file mode 100644 index 0000000000..ceedede130 --- /dev/null +++ b/apps/files_external/tests/backend/legacybackendtest.php @@ -0,0 +1,81 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Backend; + +use \OCA\Files_External\Lib\Backend\LegacyBackend; +use \OCA\Files_External\Lib\DefinitionParameter; + +class LegacyBackendTest extends \Test\TestCase { + + public function testConstructor() { + $auth = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\NullMechanism') + ->disableOriginalConstructor() + ->getMock(); + + $class = '\OC\Files\Storage\SMB'; + $definition = [ + 'configuration' => [ + 'textfield' => 'Text field', + 'passwordfield' => '*Password field', + 'checkbox' => '!Checkbox', + 'hiddenfield' => '#Hidden field', + 'optionaltext' => '&Optional text field', + 'optionalpassword' => '&*Optional password field', + ], + 'backend' => 'Backend text', + 'priority' => 123, + 'custom' => 'foo/bar.js', + 'has_dependencies' => true, + ]; + + $backend = new LegacyBackend($class, $definition, $auth); + + $this->assertEquals('\OC\Files\Storage\SMB', $backend->getStorageClass()); + $this->assertEquals('Backend text', $backend->getText()); + $this->assertEquals(123, $backend->getPriority()); + $this->assertEquals('foo/bar.js', $backend->getCustomJs()); + $this->assertEquals(true, $backend->hasDependencies()); + $this->assertArrayHasKey('null', $backend->getAuthSchemes()); + $this->assertEquals($auth, $backend->getLegacyAuthMechanism()); + + $parameters = $backend->getParameters(); + $this->assertEquals('Text field', $parameters['textfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_TEXT, $parameters['textfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['textfield']->getFlags()); + $this->assertEquals('Password field', $parameters['passwordfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_PASSWORD, $parameters['passwordfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['passwordfield']->getFlags()); + $this->assertEquals('Checkbox', $parameters['checkbox']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_BOOLEAN, $parameters['checkbox']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['checkbox']->getFlags()); + $this->assertEquals('Hidden field', $parameters['hiddenfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_HIDDEN, $parameters['hiddenfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['hiddenfield']->getFlags()); + $this->assertEquals('Optional text field', $parameters['optionaltext']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_TEXT, $parameters['optionaltext']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_OPTIONAL, $parameters['optionaltext']->getFlags()); + $this->assertEquals('Optional password field', $parameters['optionalpassword']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_PASSWORD, $parameters['optionalpassword']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_OPTIONAL, $parameters['optionalpassword']->getFlags()); + } + +} diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index 5a9683306c..2b0ee7a14e 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -1,6 +1,7 @@ + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -314,4 +315,66 @@ abstract class StoragesControllerTest extends \Test\TestCase { $this->assertEquals(Http::STATUS_OK, $response->getStatus()); $this->assertEquals($storageConfig, $response->getData()); } + + public function validateStorageProvider() { + return [ + [true, true, true], + [false, true, false], + [true, false, false], + [false, false, false] + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($backendValidate, $authMechValidate, $expectSuccess) { + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn($backendValidate); + $backend->method('isVisibleFor') + ->willReturn(true); + + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->will($this->returnValue($authMechValidate)); + + $storageConfig = new StorageConfig(); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); + + if ($expectSuccess) { + $this->service->expects($this->once()) + ->method('addStorage') + ->with($storageConfig) + ->will($this->returnValue($storageConfig)); + } else { + $this->service->expects($this->never()) + ->method('addStorage'); + } + + $response = $this->controller->create( + 'mount', + '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', + array(), + [], + [], + [], + null + ); + + if ($expectSuccess) { + $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + } else { + $this->assertEquals(Http::STATUS_UNPROCESSABLE_ENTITY, $response->getStatus()); + } + } + } diff --git a/apps/files_external/tests/definitionparameterttest.php b/apps/files_external/tests/definitionparameterttest.php new file mode 100644 index 0000000000..6be0050869 --- /dev/null +++ b/apps/files_external/tests/definitionparameterttest.php @@ -0,0 +1,70 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +use \OCA\Files_External\Lib\DefinitionParameter as Param; + +class DefinitionParameterTest extends \Test\TestCase { + + public function testJsonSerialization() { + $param = new Param('foo', 'bar'); + $this->assertEquals('bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_BOOLEAN); + $this->assertEquals('!bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_PASSWORD); + $param->setFlag(Param::FLAG_OPTIONAL); + $this->assertEquals('&*bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_HIDDEN); + $param->setFlags(Param::FLAG_NONE); + $this->assertEquals('#bar', $param->jsonSerialize()); + } + + public function validateValueProvider() { + return [ + [Param::VALUE_TEXT, Param::FLAG_NONE, 'abc', true], + [Param::VALUE_TEXT, Param::FLAG_NONE, '', false], + [Param::VALUE_TEXT, Param::FLAG_OPTIONAL, '', true], + + [Param::VALUE_BOOLEAN, Param::FLAG_NONE, false, true], + [Param::VALUE_BOOLEAN, Param::FLAG_NONE, 123, false], + + [Param::VALUE_PASSWORD, Param::FLAG_NONE, 'foobar', true], + [Param::VALUE_PASSWORD, Param::FLAG_NONE, '', false], + + [Param::VALUE_HIDDEN, Param::FLAG_NONE, '', false] + ]; + } + + /** + * @dataProvider validateValueProvider + */ + public function testValidateValue($type, $flags, $value, $success) { + $param = new Param('foo', 'bar'); + $param->setType($type); + $param->setFlags($flags); + + $this->assertEquals($success, $param->validateValue($value)); + } +} diff --git a/apps/files_external/tests/dependencytraittest.php b/apps/files_external/tests/dependencytraittest.php new file mode 100644 index 0000000000..5706d97053 --- /dev/null +++ b/apps/files_external/tests/dependencytraittest.php @@ -0,0 +1,45 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +use \OCA\Files_External\Lib\MissingDependency; + +class DependencyTraitTest extends \Test\TestCase { + + public function testCheckDependencies() { + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\DependencyTrait'); + $trait->setDependencyCheck(function() { + return [ + (new MissingDependency('dependency'))->setMessage('missing dependency'), + (new MissingDependency('program'))->setMessage('cannot find program'), + ]; + }); + + $dependencies = $trait->checkDependencies(); + $this->assertCount(2, $dependencies); + $this->assertEquals('dependency', $dependencies[0]->getDependency()); + $this->assertEquals('missing dependency', $dependencies[0]->getMessage()); + $this->assertEquals('program', $dependencies[1]->getDependency()); + $this->assertEquals('cannot find program', $dependencies[1]->getMessage()); + } + +} diff --git a/apps/files_external/tests/frontenddefinitiontraittest.php b/apps/files_external/tests/frontenddefinitiontraittest.php new file mode 100644 index 0000000000..871a87d4c5 --- /dev/null +++ b/apps/files_external/tests/frontenddefinitiontraittest.php @@ -0,0 +1,83 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +class FrontendDefinitionTraitTest extends \Test\TestCase { + + public function testJsonSerialization() { + $param = $this->getMockBuilder('\OCA\Files_External\Lib\DefinitionParameter') + ->disableOriginalConstructor() + ->getMock(); + $param->method('getName')->willReturn('foo'); + + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\FrontendDefinitionTrait'); + $trait->setText('test'); + $trait->addParameters([$param]); + $trait->setCustomJs('foo/bar.js'); + + $json = $trait->jsonSerializeDefinition(); + + $this->assertEquals('test', $json['name']); + $this->assertEquals('foo/bar.js', $json['custom']); + + $configuration = $json['configuration']; + $this->assertArrayHasKey('foo', $configuration); + } + + public function validateStorageProvider() { + return [ + [true, ['foo' => true, 'bar' => true, 'baz' => true]], + [false, ['foo' => true, 'bar' => false]] + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $params) { + $backendParams = []; + foreach ($params as $name => $valid) { + $param = $this->getMockBuilder('\OCA\Files_External\Lib\DefinitionParameter') + ->disableOriginalConstructor() + ->getMock(); + $param->method('getName') + ->willReturn($name); + $param->expects($this->once()) + ->method('validateValue') + ->willReturn($valid); + $backendParams[] = $param; + } + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + $storageConfig->expects($this->once()) + ->method('getBackendOptions') + ->willReturn([]); + + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\FrontendDefinitionTrait'); + $trait->setText('test'); + $trait->addParameters($backendParams); + + $this->assertEquals($expectedSuccess, $trait->validateStorageDefinition($storageConfig)); + } +} diff --git a/apps/files_external/tests/service/backendservicetest.php b/apps/files_external/tests/service/backendservicetest.php new file mode 100644 index 0000000000..08f6b9bf98 --- /dev/null +++ b/apps/files_external/tests/service/backendservicetest.php @@ -0,0 +1,152 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ +namespace OCA\Files_External\Tests\Service; + +use \OCA\Files_External\Service\BackendService; + +class BackendServiceTest extends \Test\TestCase { + + /** @var \OCP\IConfig */ + protected $config; + + /** @var \OCP\IL10N */ + protected $l10n; + + protected function setUp() { + $this->config = $this->getMock('\OCP\IConfig'); + $this->l10n = $this->getMock('\OCP\IL10N'); + } + + protected function getBackendMock($class) { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getIdentifier')->will($this->returnValue('identifier:'.$class)); + $backend->method('getIdentifierAliases')->will($this->returnValue(['identifier:'.$class])); + return $backend; + } + + public function testRegisterBackend() { + $service = new BackendService($this->config, $this->l10n); + + $backend = $this->getBackendMock('\Foo\Bar'); + + $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backendAlias->method('getIdentifierAliases') + ->willReturn(['identifier_real', 'identifier_alias']); + $backendAlias->method('getIdentifier') + ->willReturn('identifier_real'); + + $service->registerBackend($backend); + $service->registerBackend($backendAlias); + + $this->assertEquals($backend, $service->getBackend('identifier:\Foo\Bar')); + $this->assertEquals($backendAlias, $service->getBackend('identifier_real')); + $this->assertEquals($backendAlias, $service->getBackend('identifier_alias')); + + $backends = $service->getBackends(); + $this->assertCount(2, $backends); + $this->assertArrayHasKey('identifier:\Foo\Bar', $backends); + $this->assertArrayHasKey('identifier_real', $backends); + $this->assertArrayNotHasKey('identifier_alias', $backends); + } + + public function testUserMountingBackends() { + $this->config->expects($this->exactly(2)) + ->method('getAppValue') + ->will($this->returnValueMap([ + ['files_external', 'allow_user_mounting', 'yes', 'yes'], + ['files_external', 'user_mounting_backends', '', 'identifier:\User\Mount\Allowed,identifier_alias'] + ])); + + $service = new BackendService($this->config, $this->l10n); + + $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); + $backendAllowed->expects($this->never()) + ->method('removeVisibility'); + $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); + $backendNotAllowed->expects($this->once()) + ->method('removeVisibility') + ->with(BackendService::VISIBILITY_PERSONAL); + + $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backendAlias->method('getIdentifierAliases') + ->willReturn(['identifier_real', 'identifier_alias']); + $backendAlias->expects($this->never()) + ->method('removeVisibility'); + + $service->registerBackend($backendAllowed); + $service->registerBackend($backendNotAllowed); + $service->registerBackend($backendAlias); + } + + public function testGetAvailableBackends() { + $service = new BackendService($this->config, $this->l10n); + + $backendAvailable = $this->getBackendMock('\Backend\Available'); + $backendAvailable->expects($this->once()) + ->method('checkDependencies') + ->will($this->returnValue([])); + $backendNotAvailable = $this->getBackendMock('\Backend\NotAvailable'); + $backendNotAvailable->expects($this->once()) + ->method('checkDependencies') + ->will($this->returnValue([ + $this->getMockBuilder('\OCA\Files_External\Lib\MissingDependency') + ->disableOriginalConstructor() + ->getMock() + ])); + + $service->registerBackend($backendAvailable); + $service->registerBackend($backendNotAvailable); + + $availableBackends = $service->getAvailableBackends(); + $this->assertArrayHasKey('identifier:\Backend\Available', $availableBackends); + $this->assertArrayNotHasKey('identifier:\Backend\NotAvailable', $availableBackends); + } + + public function testGetUserBackends() { + $service = new BackendService($this->config, $this->l10n); + + $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); + $backendAllowed->expects($this->once()) + ->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->will($this->returnValue(true)); + $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); + $backendNotAllowed->expects($this->once()) + ->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->will($this->returnValue(false)); + + $service->registerBackend($backendAllowed); + $service->registerBackend($backendNotAllowed); + + $userBackends = $service->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL); + $this->assertArrayHasKey('identifier:\User\Mount\Allowed', $userBackends); + $this->assertArrayNotHasKey('identifier:\User\Mount\NotAllowed', $userBackends); + } + +} + diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index f6ed9f1422..05585d2c06 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -827,6 +827,46 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { $this->assertEquals(['preview' => true], $storage4->getMountOptions()); } + public function testReadLegacyConfigNoAuthMechanism() { + $configFile = $this->dataDir . '/mount.json'; + + $json = [ + 'user' => [ + 'user1' => [ + '/$user/files/somemount' => [ + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + '/$user/files/othermount' => [ + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + // no authMechanism + 'options' => [], + 'mountOptions' => [], + ], + ] + ] + ]; + + file_put_contents($configFile, json_encode($json)); + + $allStorages = $this->service->getAllStorages(); + + $this->assertCount(2, $allStorages); + + $storage1 = $allStorages[1]; + $storage2 = $allStorages[2]; + + $this->assertEquals('/somemount', $storage1->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage1->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage1->getAuthMechanism()->getIdentifier()); + + $this->assertEquals('/othermount', $storage2->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage2->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Other\Auth\Mechanism', $storage2->getAuthMechanism()->getIdentifier()); + } + public function testReadLegacyConfigClass() { $configFile = $this->dataDir . '/mount.json'; diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 397f2a2e5c..07286106c7 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -1,6 +1,7 @@ + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -241,6 +242,64 @@ abstract class StoragesServiceTest extends \Test\TestCase { $this->service->removeStorage(255); } + public function testCreateStorage() { + $mountPoint = 'mount'; + $backendIdentifier = 'identifier:\OCA\Files_External\Lib\Backend\SMB'; + $authMechanismIdentifier = 'identifier:\Auth\Mechanism'; + $backendOptions = ['param' => 'foo', 'param2' => 'bar']; + $mountOptions = ['option' => 'foobar']; + $applicableUsers = ['user1', 'user2']; + $applicableGroups = ['group']; + $priority = 123; + + $backend = $this->backendService->getBackend($backendIdentifier); + $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier); + + $storage = $this->service->createStorage( + $mountPoint, + $backendIdentifier, + $authMechanismIdentifier, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + + $this->assertEquals('/'.$mountPoint, $storage->getMountPoint()); + $this->assertEquals($backend, $storage->getBackend()); + $this->assertEquals($authMechanism, $storage->getAuthMechanism()); + $this->assertEquals($backendOptions, $storage->getBackendOptions()); + $this->assertEquals($mountOptions, $storage->getMountOptions()); + $this->assertEquals($applicableUsers, $storage->getApplicableUsers()); + $this->assertEquals($applicableGroups, $storage->getApplicableGroups()); + $this->assertEquals($priority, $storage->getPriority()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateStorageInvalidClass() { + $this->service->createStorage( + 'mount', + 'identifier:\OC\Not\A\Backend', + 'identifier:\Auth\Mechanism', + [] + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateStorageInvalidAuthMechanismClass() { + $this->service->createStorage( + 'mount', + 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'identifier:\Not\An\Auth\Mechanism', + [] + ); + } + public static function createHookCallback($params) { self::$hookCalls[] = array( 'signal' => Filesystem::signal_create_mount, diff --git a/apps/files_external/tests/service/userglobalstoragesservicetest.php b/apps/files_external/tests/service/userglobalstoragesservicetest.php new file mode 100644 index 0000000000..49a0245384 --- /dev/null +++ b/apps/files_external/tests/service/userglobalstoragesservicetest.php @@ -0,0 +1,215 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ +namespace OCA\Files_External\Tests\Service; + +use \OCA\Files_External\Service\UserGlobalStoragesService; +use \OCP\IGroupManager; + +use \OCA\Files_External\Lib\StorageConfig; + +class UserGlobalStoragesServiceTest extends GlobalStoragesServiceTest { + + protected $groupManager; + + protected $globalStoragesService; + + protected $user; + + const USER_ID = 'test_user'; + const GROUP_ID = 'test_group'; + + public function setUp() { + parent::setUp(); + + $this->globalStoragesService = $this->service; + + $this->user = new \OC\User\User(self::USER_ID, null); + $userSession = $this->getMock('\OCP\IUserSession'); + $userSession + ->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($this->user)); + + $this->groupManager = $this->getMock('\OCP\IGroupManager'); + $this->groupManager->method('isInGroup') + ->will($this->returnCallback(function($userId, $groupId) { + if ($userId === self::USER_ID && $groupId === self::GROUP_ID) { + return true; + } + return false; + })); + + $this->service = new UserGlobalStoragesService( + $this->backendService, + $userSession, + $this->groupManager + ); + } + + public function applicableStorageProvider() { + return [ + [[], [], true], + + // not applicable cases + [['user1'], [], false], + [[], ['group1'], false], + [['user1'], ['group1'], false], + + // applicable cases + [[self::USER_ID], [], true], + [[], [self::GROUP_ID], true], + [[self::USER_ID], ['group1'], true], + [['user1'], [self::GROUP_ID], true], + + // sanity checks + [['user1', 'user2', self::USER_ID, 'user3'], [], true], + ]; + } + + /** + * @dataProvider applicableStorageProvider + */ + public function testGetStorageWithApplicable($applicableUsers, $applicableGroups, $isVisible) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + $storage->setApplicableUsers($applicableUsers); + $storage->setApplicableGroups($applicableGroups); + + $newStorage = $this->globalStoragesService->addStorage($storage); + + $storages = $this->service->getAllStorages(); + if ($isVisible) { + $this->assertEquals(1, count($storages)); + $retrievedStorage = $this->service->getStorage($newStorage->getId()); + $this->assertEquals('/mountpoint', $retrievedStorage->getMountPoint()); + } else { + $this->assertEquals(0, count($storages)); + } + + } + + /** + * @expectedException \DomainException + */ + public function testAddStorage($storageParams = null) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $this->service->addStorage($storage); + } + + /** + * @expectedException \DomainException + */ + public function testUpdateStorage($storageParams = null) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->globalStoragesService->addStorage($storage); + + $retrievedStorage = $this->service->getStorage($newStorage->getId()); + $retrievedStorage->setMountPoint('abc'); + $this->service->updateStorage($retrievedStorage); + } + + /** + * @expectedException \DomainException + */ + public function testDeleteStorage() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->globalStoragesService->addStorage($storage); + $this->assertEquals(1, $newStorage->getId()); + + $this->service->removeStorage(1); + } + + public function testHooksAddStorage($a = null, $b = null, $c = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksUpdateStorage($a = null, $b = null, $c = null, $d = null, $e = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksRenameMountPoint() { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksDeleteStorage($a = null, $b = null, $c = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testLegacyConfigConversionApplicableAll() { + // we don't test this here + $this->assertTrue(true); + } + + public function testLegacyConfigConversionApplicableUserAndGroup() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigAndGenerateConfigId() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigNoAuthMechanism() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigClass() { + // we don't test this here + $this->assertTrue(true); + } + +} From 97dbc79c16ba9d4d6c361d6f397908ef7893954d Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 09:21:09 +0100 Subject: [PATCH 06/10] Compatibility shims for OC_Mount_Config The following functions have been removed: - addMountPoint() - removeMountPoint() - movePersonalMountPoint() registerBackend() has been rewritten as a shim around BackendService, allowing legacy code to interact with the new API seamlessly addMountPoint() was already disconnected from all production code, so this commit completes the job and removes the function itself, along with disconnecting and removing related functions. Unit tests have likewise been removed. getAbsoluteMountPoints(), getSystemMountPoints() and getPersonalMountPoints() have been rewritten to use the StoragesServices --- apps/files_external/appinfo/application.php | 3 + .../files_external/lib/auth/authmechanism.php | 1 + apps/files_external/lib/auth/builtin.php | 41 + .../lib/backend/legacybackend.php | 85 ++ apps/files_external/lib/config.php | 545 ++------ .../tests/backend/legacybackendtest.php | 4 +- apps/files_external/tests/mountconfig.php | 1157 ----------------- settings/personal.php | 5 +- 8 files changed, 235 insertions(+), 1606 deletions(-) create mode 100644 apps/files_external/lib/auth/builtin.php create mode 100644 apps/files_external/lib/backend/legacybackend.php delete mode 100644 apps/files_external/tests/mountconfig.php diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php index 19da1f724b..38b9e9b7c3 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -72,6 +72,9 @@ class Application extends App { $service->registerAuthMechanisms([ // AuthMechanism::SCHEME_NULL mechanism $container->query('OCA\Files_External\Lib\Auth\NullMechanism'), + + // AuthMechanism::SCHEME_BUILTIN mechanism + $container->query('OCA\Files_External\Lib\Auth\Builtin'), ]); } diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php index a89ee823d5..11d99bb330 100644 --- a/apps/files_external/lib/auth/authmechanism.php +++ b/apps/files_external/lib/auth/authmechanism.php @@ -51,6 +51,7 @@ class AuthMechanism implements \JsonSerializable { /** Standard authentication schemes */ const SCHEME_NULL = 'null'; + const SCHEME_BUILTIN = 'builtin'; const SCHEME_PASSWORD = 'password'; const SCHEME_OAUTH1 = 'oauth1'; const SCHEME_OAUTH2 = 'oauth2'; diff --git a/apps/files_external/lib/auth/builtin.php b/apps/files_external/lib/auth/builtin.php new file mode 100644 index 0000000000..ee28a4e8a5 --- /dev/null +++ b/apps/files_external/lib/auth/builtin.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_external\Lib\StorageConfig; + +/** + * Builtin authentication mechanism, for legacy backends + */ +class Builtin extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('builtin::builtin') + ->setScheme(self::SCHEME_BUILTIN) + ->setText($l->t('Builtin')) + ; + } + +} diff --git a/apps/files_external/lib/backend/legacybackend.php b/apps/files_external/lib/backend/legacybackend.php new file mode 100644 index 0000000000..0f60c2caa4 --- /dev/null +++ b/apps/files_external/lib/backend/legacybackend.php @@ -0,0 +1,85 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\Builtin; + +/** + * Legacy compatibility for OC_Mount_Config::registerBackend() + */ +class LegacyBackend extends Backend { + + /** + * @param string $class + * @param array $definition + * @param Builtin $authMechanism + */ + public function __construct($class, array $definition, Builtin $authMechanism) { + $this + ->setIdentifier($class) + ->setStorageClass($class) + ->setText($definition['backend']) + ->addAuthScheme(Builtin::SCHEME_BUILTIN) + ->setLegacyAuthMechanism($authMechanism) + ; + + foreach ($definition['configuration'] as $name => $placeholder) { + $flags = DefinitionParameter::FLAG_NONE; + $type = DefinitionParameter::VALUE_TEXT; + if ($placeholder[0] === '&') { + $flags = DefinitionParameter::FLAG_OPTIONAL; + $placeholder = substr($placeholder, 1); + } + switch ($placeholder[0]) { + case '!': + $type = DefinitionParameter::VALUE_BOOLEAN; + $placeholder = substr($placeholder, 1); + break; + case '*': + $type = DefinitionParameter::VALUE_PASSWORD; + $placeholder = substr($placeholder, 1); + break; + case '#': + $type = DefinitionParameter::VALUE_HIDDEN; + $placeholder = substr($placeholder, 1); + break; + } + $this->addParameter((new DefinitionParameter($name, $placeholder)) + ->setType($type) + ->setFlags($flags) + ); + } + + if (isset($definition['priority'])) { + $this->setPriority($definition['priority']); + } + if (isset($definition['custom'])) { + $this->setCustomJs($definition['custom']); + } + if (isset($definition['has_dependencies']) && $definition['has_dependencies']) { + $this->setDependencyCheck($class . '::checkDependencies'); + } + } + +} diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index c0ded522de..6fdf050fb6 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -35,6 +35,8 @@ use phpseclib\Crypt\AES; use \OCP\AppFramework\IAppContainer; use \OCA\Files_External\Lib\BackendConfig; use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Backend\LegacyBackend; +use \OCA\Files_External\Lib\StorageConfig; /** * Class to configure mount.json globally and for users @@ -66,6 +68,21 @@ class OC_Mount_Config { self::$appContainer = $appContainer; } + /** + * @param string $class + * @param array $definition + * @return bool + * @deprecated 8.2.0 use \OCA\Files_External\Service\BackendService::registerBackend() + */ + public static function registerBackend($class, $definition) { + $backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService'); + $auth = self::$appContainer->query('OCA\Files_External\Lib\Auth\Builtin'); + + $backendService->registerBackend(new LegacyBackend($class, $definition, $auth)); + + return true; + } + /* * Hook that mounts the given user's visible mount points * @@ -102,172 +119,107 @@ class OC_Mount_Config { /** * Returns the mount points for the given user. * The mount point is relative to the data directory. - * TODO: Move me into StoragesService * - * @param string $user user + * @param string $uid user * @return array of mount point string as key, mountpoint config as value + * + * @deprecated 8.2.0 use UserGlobalStoragesService::getAllStorages() and UserStoragesService::getAllStorages() */ - public static function getAbsoluteMountPoints($user) { + public static function getAbsoluteMountPoints($uid) { $mountPoints = array(); - $backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService'); - // Load system mount points - $mountConfig = self::readData(); + $userGlobalStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserGlobalStoragesService'); + $userStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserStoragesService'); + $user = self::$appContainer->query('OCP\IUserManager')->get($uid); - // Global mount points (is this redundant?) - if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) { - foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) { - if (!isset($options['backend'])) { - $options['backend'] = $options['class']; - } - $backend = $backendService->getBackend($options['backend']); - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backend->getPriority(); - } - if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); - } + $userGlobalStoragesService->setUser($user); + $userStoragesService->setUser($user); - // Override if priority greater - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backend->getText(); - $mountPoints[$mountPoint] = $options; - } - } - } - // All user mount points - if (isset($mountConfig[self::MOUNT_TYPE_USER]) && isset($mountConfig[self::MOUNT_TYPE_USER]['all'])) { - $mounts = $mountConfig[self::MOUNT_TYPE_USER]['all']; - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - if (!isset($options['backend'])) { - $options['backend'] = $options['class']; - } - $backend = $backendService->getBackend($options['backend']); - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backend->getPriority(); - } - if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); - } - - // Override if priority greater - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backend->getText(); - $mountPoints[$mountPoint] = $options; - } - } - } - // Group mount points - if (isset($mountConfig[self::MOUNT_TYPE_GROUP])) { - foreach ($mountConfig[self::MOUNT_TYPE_GROUP] as $group => $mounts) { - if (\OC_Group::inGroup($user, $group)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - if (!isset($options['backend'])) { - $options['backend'] = $options['class']; - } - $backend = $backendService->getBackend($options['backend']); - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backend->getPriority(); - } - if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); - } - - // Override if priority greater or if priority type different - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_GROUP) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GROUP; - $options['backend'] = $backend->getText(); - $mountPoints[$mountPoint] = $options; - } - } - } - } - } - // User mount points - if (isset($mountConfig[self::MOUNT_TYPE_USER])) { - foreach ($mountConfig[self::MOUNT_TYPE_USER] as $mountUser => $mounts) { - if (strtolower($mountUser) === strtolower($user)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - if (!isset($options['backend'])) { - $options['backend'] = $options['class']; - } - $backend = $backendService->getBackend($options['backend']); - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backend->getPriority(); - } - if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); - } - - // Override if priority greater or if priority type different - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_USER) - ) { - $options['priority_type'] = self::MOUNT_TYPE_USER; - $options['backend'] = $backend->getText(); - $mountPoints[$mountPoint] = $options; - } - } - } + foreach ($userGlobalStoragesService->getAllStorages() as $storage) { + $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); + $mountEntry = self::prepareMountPointEntry($storage, false); + foreach ($mountEntry['options'] as &$option) { + $option = self::setUserVars($uid, $option); } + $mountPoints[$mountPoint] = $mountEntry; } - // Load personal mount points - $mountConfig = self::readData($user); - if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) { - foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) { - if (!isset($options['backend'])) { - $options['backend'] = $options['class']; - } - $backend = $backendService->getBackend($options['backend']); - if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { - $options['personal'] = true; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['authMechanism'])) { - $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getIdentifier(); - } - - // Always override previous config - $options['priority_type'] = self::MOUNT_TYPE_PERSONAL; - $options['backend'] = $backend->getText(); - $mountPoints[$mountPoint] = $options; - } + foreach ($userStoragesService->getAllStorages() as $storage) { + $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); + $mountEntry = self::prepareMountPointEntry($storage, true); + foreach ($mountEntry['options'] as &$option) { + $option = self::setUserVars($uid, $option); } + $mountPoints[$mountPoint] = $mountEntry; } + $userGlobalStoragesService->resetUser(); + $userStoragesService->resetUser(); + return $mountPoints; } + /** + * Get the system mount points + * + * @return array + * + * @deprecated 8.2.0 use GlobalStoragesService::getAllStorages() + */ + public static function getSystemMountPoints() { + $mountPoints = []; + $service = self::$appContainer->query('OCA\Files_External\Service\GlobalStoragesService'); + + foreach ($service->getAllStorages() as $storage) { + $mountPoints[] = self::prepareMountPointEntry($storage, false); + } + } + + /** + * Get the personal mount points of the current user + * + * @return array + * + * @deprecated 8.2.0 use UserStoragesService::getAllStorages() + */ + public static function getPersonalMountPoints() { + $mountPoints = []; + $service = self::$appContainer->query('OCA\Files_External\Service\UserStoragesService'); + + foreach ($service->getAllStorages() as $storage) { + $mountPoints[] = self::prepareMountPointEntry($storage, true); + } + } + + /** + * Convert a StorageConfig to the legacy mountPoints array format + * There's a lot of extra information in here, to satisfy all of the legacy functions + * + * @param StorageConfig $storage + * @param bool $isPersonal + * @return array + */ + private static function prepareMountPointEntry(StorageConfig $storage, $isPersonal) { + $mountEntry = []; + + $mountEntry['mountpoint'] = substr($storage->getMountPoint(), 1); // remove leading slash + $mountEntry['class'] = $storage->getBackend()->getIdentifier(); + $mountEntry['backend'] = $storage->getBackend()->getText(); + $mountEntry['authMechanism'] = $storage->getAuthMechanism()->getIdentifier(); + $mountEntry['personal'] = $isPersonal; + $mountEntry['options'] = self::decryptPasswords($storage->getBackendOptions()); + $mountEntry['mountOptions'] = $storage->getMountOptions(); + $mountEntry['priority'] = $storage->getPriority(); + $mountEntry['applicable'] = [ + 'groups' => $storage->getApplicableGroups(), + 'users' => $storage->getApplicableUsers(), + ]; + $mountEntry['id'] = $storage->getId(); + // $mountEntry['storage_id'] = null; // we don't store this! + + return $mountEntry; + } + /** * fill in the correct values for $user * @@ -288,147 +240,6 @@ class OC_Mount_Config { return $input; } - /** - * Get the system mount points - * The returned array is not in the same format as getUserMountPoints() - * - * @return array - */ - public static function getSystemMountPoints() { - $mountPoints = self::readData(); - $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); - $system = array(); - if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) { - foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) { - foreach ($mounts as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $backend = $backendService->getBackend($mount['class']); - $mount['options'] = self::decryptPasswords($mount['options']); - if (!isset($mount['priority'])) { - $mount['priority'] = $backend->getPriority(); - } - // Remove '/$user/files/' from mount point - $mountPoint = substr($mountPoint, 13); - - $config = array( - 'class' => $mount['class'], - 'mountpoint' => $mountPoint, - 'backend' => $backend->getText(), - 'priority' => $mount['priority'], - 'options' => $mount['options'], - 'applicable' => array('groups' => array($group), 'users' => array()) - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $hash = self::makeConfigHash($config); - // If an existing config exists (with same class, mountpoint and options) - if (isset($system[$hash])) { - // add the groups into that config - $system[$hash]['applicable']['groups'] - = array_merge($system[$hash]['applicable']['groups'], array($group)); - } else { - $system[$hash] = $config; - } - } - } - } - if (isset($mountPoints[self::MOUNT_TYPE_USER])) { - foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) { - foreach ($mounts as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $backend = $backendService->getBackend($mount['class']); - $mount['options'] = self::decryptPasswords($mount['options']); - if (!isset($mount['priority'])) { - $mount['priority'] = $backend->getPriority(); - } - // Remove '/$user/files/' from mount point - $mountPoint = substr($mountPoint, 13); - $config = array( - 'class' => $mount['class'], - 'mountpoint' => $mountPoint, - 'backend' => $backend->getText(), - 'priority' => $mount['priority'], - 'options' => $mount['options'], - 'applicable' => array('groups' => array(), 'users' => array($user)) - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $hash = self::makeConfigHash($config); - // If an existing config exists (with same class, mountpoint and options) - if (isset($system[$hash])) { - // add the users into that config - $system[$hash]['applicable']['users'] - = array_merge($system[$hash]['applicable']['users'], array($user)); - } else { - $system[$hash] = $config; - } - } - } - } - return array_values($system); - } - - /** - * Get the personal mount points of the current user - * The returned array is not in the same format as getUserMountPoints() - * - * @return array - */ - public static function getPersonalMountPoints() { - $mountPoints = self::readData(OCP\User::getUser()); - $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); - $uid = OCP\User::getUser(); - $personal = array(); - if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) { - foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $backend = $backendService->getBackend($mount['class']); - $mount['options'] = self::decryptPasswords($mount['options']); - $config = array( - 'class' => $mount['class'], - // Remove '/uid/files/' from mount point - 'mountpoint' => substr($mountPoint, strlen($uid) + 8), - 'backend' => $backend->getText(), - 'options' => $mount['options'] - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $personal[] = $config; - } - } - return $personal; - } - /** * Test connecting using the given backend configuration * @@ -464,158 +275,6 @@ class OC_Mount_Config { return self::STATUS_ERROR; } - /** - * Add a mount point to the filesystem - * - * @param string $mountPoint Mount point - * @param string $class Backend class - * @param array $classOptions Backend parameters for the class - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @param string $applicable User or group to apply mount to - * @param bool $isPersonal Personal or system mount point i.e. is this being called from the personal or admin page - * @param int|null $priority Mount point priority, null for default - * @return boolean - * - * @deprecated use StoragesService#addStorage() instead - */ - public static function addMountPoint($mountPoint, - $class, - $classOptions, - $mountType, - $applicable, - $isPersonal = false, - $priority = null) { - $backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService'); - $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); - $relMountPoint = $mountPoint; - if ($mountPoint === '' || $mountPoint === '/') { - // can't mount at root folder - return false; - } - - $backend = $backendService->getBackend($class); - if (!isset($backend)) { - // invalid backend - return false; - } - if ($isPersonal) { - // Verify that the mount point applies for the current user - // Prevent non-admin users from mounting local storage and other disabled backends - if ($applicable != OCP\User::getUser() || !$backend->isVisibleFor(BackendConfig::VISIBILITY_PERSONAL)) { - return false; - } - $mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/'); - } else { - $mountPoint = '/$user/files/' . ltrim($mountPoint, '/'); - } - - $mount = array($applicable => array( - $mountPoint => array( - 'class' => $class, - 'options' => self::encryptPasswords($classOptions)) - ) - ); - if (!$isPersonal && !is_null($priority)) { - $mount[$applicable][$mountPoint]['priority'] = $priority; - } - - $mountPoints = self::readData($isPersonal ? OCP\User::getUser() : null); - // who else loves multi-dimensional array ? - $isNew = !isset($mountPoints[$mountType]) || - !isset($mountPoints[$mountType][$applicable]) || - !isset($mountPoints[$mountType][$applicable][$mountPoint]); - $mountPoints = self::mergeMountPoints($mountPoints, $mount, $mountType); - - // Set default priority if none set - if (!isset($mountPoints[$mountType][$applicable][$mountPoint]['priority'])) { - $mountPoints[$mountType][$applicable][$mountPoint]['priority'] - = $backend->getPriority(); - } - - self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints); - - $result = self::getBackendStatus($class, $classOptions, $isPersonal); - if ($result === self::STATUS_SUCCESS && $isNew) { - \OC_Hook::emit( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_create_mount, - array( - \OC\Files\Filesystem::signal_param_path => $relMountPoint, - \OC\Files\Filesystem::signal_param_mount_type => $mountType, - \OC\Files\Filesystem::signal_param_users => $applicable, - ) - ); - } - return $result; - } - - /** - * - * @param string $mountPoint Mount point - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @param string $applicable User or group to remove mount from - * @param bool $isPersonal Personal or system mount point - * @return bool - * - * @deprecated use StoragesService#removeStorage() instead - */ - public static function removeMountPoint($mountPoint, $mountType, $applicable, $isPersonal = false) { - // Verify that the mount point applies for the current user - $relMountPoints = $mountPoint; - if ($isPersonal) { - if ($applicable != OCP\User::getUser()) { - return false; - } - $mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/'); - } else { - $mountPoint = '/$user/files/' . ltrim($mountPoint, '/'); - } - $mountPoint = \OC\Files\Filesystem::normalizePath($mountPoint); - $mountPoints = self::readData($isPersonal ? OCP\User::getUser() : null); - // Remove mount point - unset($mountPoints[$mountType][$applicable][$mountPoint]); - // Unset parent arrays if empty - if (empty($mountPoints[$mountType][$applicable])) { - unset($mountPoints[$mountType][$applicable]); - if (empty($mountPoints[$mountType])) { - unset($mountPoints[$mountType]); - } - } - self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints); - \OC_Hook::emit( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_delete_mount, - array( - \OC\Files\Filesystem::signal_param_path => $relMountPoints, - \OC\Files\Filesystem::signal_param_mount_type => $mountType, - \OC\Files\Filesystem::signal_param_users => $applicable, - ) - ); - return true; - } - - /** - * - * @param string $mountPoint Mount point - * @param string $target The new mount point - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @return bool - */ - public static function movePersonalMountPoint($mountPoint, $target, $mountType) { - $mountPoint = rtrim($mountPoint, '/'); - $user = OCP\User::getUser(); - $mountPoints = self::readData($user); - if (!isset($mountPoints[$mountType][$user][$mountPoint])) { - return false; - } - $mountPoints[$mountType][$user][$target] = $mountPoints[$mountType][$user][$mountPoint]; - // Remove old mount point - unset($mountPoints[$mountType][$user][$mountPoint]); - - self::writeData($user, $mountPoints); - return true; - } - /** * Read the mount points in the config file into an array * diff --git a/apps/files_external/tests/backend/legacybackendtest.php b/apps/files_external/tests/backend/legacybackendtest.php index ceedede130..44cb16a498 100644 --- a/apps/files_external/tests/backend/legacybackendtest.php +++ b/apps/files_external/tests/backend/legacybackendtest.php @@ -27,7 +27,7 @@ use \OCA\Files_External\Lib\DefinitionParameter; class LegacyBackendTest extends \Test\TestCase { public function testConstructor() { - $auth = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\NullMechanism') + $auth = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\Builtin') ->disableOriginalConstructor() ->getMock(); @@ -54,7 +54,7 @@ class LegacyBackendTest extends \Test\TestCase { $this->assertEquals(123, $backend->getPriority()); $this->assertEquals('foo/bar.js', $backend->getCustomJs()); $this->assertEquals(true, $backend->hasDependencies()); - $this->assertArrayHasKey('null', $backend->getAuthSchemes()); + $this->assertArrayHasKey('builtin', $backend->getAuthSchemes()); $this->assertEquals($auth, $backend->getLegacyAuthMechanism()); $parameters = $backend->getParameters(); diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php deleted file mode 100644 index b76ba0a39a..0000000000 --- a/apps/files_external/tests/mountconfig.php +++ /dev/null @@ -1,1157 +0,0 @@ - - * @author Joas Schilling - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see - * - */ - -class Test_Mount_Config_Dummy_Storage extends \OC\Files\Storage\Common { - public function __construct($params) { - if (isset($params['simulateFail']) && $params['simulateFail'] == true) { - throw new \Exception('Simulated config validation fail'); - } - } - - public function getId() { - return 'dummy_storage'; - } - - public function mkdir($path) { - return false; - } - - public function rmdir($path) { - return false; - } - - public function opendir($path) { - return false; - } - - public function filetype($path) { - return false; - } - - public function file_exists($path) { - return false; - } - - public function unlink($path) { - return false; - } - - public function fopen($path, $mode) { - return false; - } - - public function touch($path, $mtime = null) { - return false; - } - - public function stat($path) { - return false; - } - - public function test() { - return true; - } -} - -class Test_Mount_Config_Storage_No_Personal extends Test_Mount_Config_Dummy_Storage { -} - -class Test_Mount_Config_Hook_Test { - static $signal; - static $params; - - public static function setUpHooks() { - self::clear(); - \OCP\Util::connectHook( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_create_mount, - '\Test_Mount_Config_Hook_Test', 'createHookCallback'); - \OCP\Util::connectHook( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_delete_mount, - '\Test_Mount_Config_Hook_Test', 'deleteHookCallback'); - } - - public static function clear() { - self::$signal = null; - self::$params = null; - } - - public static function createHookCallback($params) { - self::$signal = \OC\Files\Filesystem::signal_create_mount; - self::$params = $params; - } - - public static function deleteHookCallback($params) { - self::$signal = \OC\Files\Filesystem::signal_delete_mount; - self::$params = $params; - } - - public static function getLastCall() { - return array(self::$signal, self::$params); - } -} - -/** - * Class Test_Mount_Config - */ -class Test_Mount_Config extends \Test\TestCase { - - private $dataDir; - private $userHome; - private $oldAllowedBackends; - - const TEST_USER1 = 'user1'; - const TEST_USER2 = 'user2'; - const TEST_GROUP1 = 'group1'; - const TEST_GROUP1B = 'group1b'; - const TEST_GROUP2 = 'group2'; - const TEST_GROUP2B = 'group2b'; - - protected function setUp() { - parent::setUp(); - - OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Storage', array( - 'backend' => 'dummy', - 'priority' => 150, - 'configuration' => array() - ) - ); - OC_Mount_Config::registerBackend('Test_Mount_Config_Storage_No_Personal', array( - 'backend' => 'dummy no personal', - 'priority' => 150, - 'configuration' => array() - ) - ); - - \OC_User::createUser(self::TEST_USER1, self::TEST_USER1); - \OC_User::createUser(self::TEST_USER2, self::TEST_USER2); - - \OC_Group::createGroup(self::TEST_GROUP1); - \OC_Group::createGroup(self::TEST_GROUP1B); - \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1); - \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1B); - \OC_Group::createGroup(self::TEST_GROUP2); - \OC_Group::createGroup(self::TEST_GROUP2B); - \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2); - \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2B); - - \OC_User::setUserId(self::TEST_USER1); - $this->userHome = \OC_User::getHome(self::TEST_USER1); - @mkdir($this->userHome); - - $this->dataDir = \OC_Config::getValue( - 'datadirectory', - \OC::$SERVERROOT . '/data/' - ); - $this->oldAllowedBackends = OCP\Config::getAppValue( - 'files_external', - 'user_mounting_backends', - '' - ); - OCP\Config::setAppValue( - 'files_external', - 'user_mounting_backends', - 'Test_Mount_Config_Dummy_Storage' - ); - - OC_Mount_Config::$skipTest = true; - Test_Mount_Config_Hook_Test::setupHooks(); - } - - protected function tearDown() { - Test_Mount_Config_Hook_Test::clear(); - OC_Mount_Config::$skipTest = false; - - \OC_User::deleteUser(self::TEST_USER2); - \OC_User::deleteUser(self::TEST_USER1); - \OC_Group::deleteGroup(self::TEST_GROUP1); - \OC_Group::deleteGroup(self::TEST_GROUP1B); - \OC_Group::deleteGroup(self::TEST_GROUP2); - \OC_Group::deleteGroup(self::TEST_GROUP2B); - - @unlink($this->dataDir . '/mount.json'); - - OCP\Config::setAppValue( - 'files_external', - 'user_mounting_backends', - $this->oldAllowedBackends - ); - - parent::tearDown(); - } - - /** - * Reads the global config, for checking - */ - private function readGlobalConfig() { - $configFile = $this->dataDir . '/mount.json'; - return json_decode(file_get_contents($configFile), true); - } - - private function writeGlobalConfig($config) { - $configFile = $this->dataDir . '/mount.json'; - file_put_contents($configFile, json_encode($config)); - } - - /** - * Reads the user config, for checking - */ - private function readUserConfig() { - $configFile = $this->userHome . '/mount.json'; - return json_decode(file_get_contents($configFile), true); - } - - /** - * Write the user config, to simulate existing files - */ - private function writeUserConfig($config) { - $configFile = $this->userHome . '/mount.json'; - file_put_contents($configFile, json_encode($config)); - } - - /** - * Test mount point validation - */ - public function testAddMountPointValidation() { - $storageClass = 'Test_Mount_Config_Dummy_Storage'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - $this->assertFalse(OC_Mount_Config::addMountPoint('', $storageClass, array(), $mountType, $applicable, $isPersonal)); - $this->assertFalse(OC_Mount_Config::addMountPoint('/', $storageClass, array(), $mountType, $applicable, $isPersonal)); - } - - /** - * Test adding a global mount point - */ - public function testAddGlobalMountPoint() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = 'all'; - $isPersonal = false; - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - $this->assertEquals(0, OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Dummy_Storage', $storageOptions, $mountType, $applicable, $isPersonal)); - - $config = $this->readGlobalConfig(); - $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config[$mountType])); - $this->assertTrue(isset($config[$mountType][$applicable])); - $this->assertTrue(isset($config[$mountType][$applicable]['/$user/files/ext'])); - $this->assertEquals( - 'Test_Mount_Config_Dummy_Storage', - $config[$mountType][$applicable]['/$user/files/ext']['class'] - ); - } - - /** - * Test adding a personal mount point - */ - public function testAddMountPointSingleUser() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - $this->assertEquals(0, OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Dummy_Storage', $storageOptions, $mountType, $applicable, $isPersonal)); - - $config = $this->readUserConfig(); - $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config[$mountType])); - $this->assertTrue(isset($config[$mountType][$applicable])); - $this->assertTrue(isset($config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext'])); - $this->assertEquals( - 'Test_Mount_Config_Dummy_Storage', - $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['class'] - ); - } - - /** - * Test adding a personal mount point using disallowed backend - */ - public function testAddDisallowedBackendMountPointSingleUser() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - - // local - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', '\OC\Files\Storage\Local', array(), $mountType, $applicable, $isPersonal)); - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - // non-local but forbidden - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Storage_No_Personal', $storageOptions, $mountType, $applicable, $isPersonal)); - - $this->assertFalse(file_exists($this->userHome . '/mount.json')); - } - - /** - * Test adding a mount point with an non-existant backend - */ - public function testAddMountPointUnexistClass() { - $storageClass = 'Unexist_Storage'; - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = false; - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', $storageClass, array(), $mountType, $applicable, $isPersonal)); - - } - - /** - * Provider for testing configurations with different - * "applicable" values (all, user, groups) - */ - public function applicableConfigProvider() { - return array( - // applicable to "all" - array( - OC_Mount_Config::MOUNT_TYPE_USER, - 'all', - array( - 'users' => array('all'), - 'groups' => array() - ) - ), - // applicable to single user - array( - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - array( - 'users' => array(self::TEST_USER1), - 'groups' => array() - ) - ), - // applicable to single group - array( - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - array( - 'users' => array(), - 'groups' => array(self::TEST_GROUP1) - ) - ), - ); - } - - /** - * Test reading and writing global config - * - * @dataProvider applicableConfigProvider - */ - public function testReadWriteGlobalConfig($mountType, $applicable, $expectApplicableArray) { - - $mountType = $mountType; - $applicable = $applicable; - $isPersonal = false; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - $mountType, - $applicable, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($expectApplicableArray, $config[0]['applicable']); - $savedOptions = $config[0]['options']; - $this->assertEquals($options, $savedOptions); - // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($options), array_keys($savedOptions)); - } - - /** - * Test reading and writing config - */ - public function testReadWritePersonalConfig() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - $mountType, - $applicable, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getPersonalMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $savedOptions = $config[0]['options']; - $this->assertEquals($options, $savedOptions); - // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($options), array_keys($savedOptions)); - } - - public function testHooks() { - $mountPoint = '/test'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - $mountPoint, - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - \OC\Files\Filesystem::signal_create_mount, - $hookName - ); - $this->assertEquals( - $mountPoint, - $params[\OC\Files\Filesystem::signal_param_path] - ); - $this->assertEquals( - $mountType, - $params[\OC\Files\Filesystem::signal_param_mount_type] - ); - $this->assertEquals( - $applicable, - $params[\OC\Files\Filesystem::signal_param_users] - ); - - Test_Mount_Config_Hook_Test::clear(); - - // edit - $mountConfig['host'] = 'anothersmbhost'; - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - $mountPoint, - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // hook must not be called on edit - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - null, - $hookName - ); - - Test_Mount_Config_Hook_Test::clear(); - - $this->assertTrue( - OC_Mount_Config::removeMountPoint( - $mountPoint, - $mountType, - $applicable, - $isPersonal - ) - ); - - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - \OC\Files\Filesystem::signal_delete_mount, - $hookName - ); - $this->assertEquals( - $mountPoint, - $params[\OC\Files\Filesystem::signal_param_path] - ); - $this->assertEquals( - $mountType, - $params[\OC\Files\Filesystem::signal_param_mount_type] - ); - $this->assertEquals( - $applicable, - $params[\OC\Files\Filesystem::signal_param_users] - ); - } - - /** - * Test password obfuscation - */ - public function testPasswordObfuscation() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // note: password re-reading is covered by testReadWritePersonalConfig - - // check that password inside the file is NOT in plain text - $config = $this->readUserConfig(); - $savedConfig = $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options']; - - // no more clear text password in file (kept because of key order) - $this->assertEquals('', $savedConfig['password']); - - // encrypted password is present - $this->assertNotEquals($mountConfig['password'], $savedConfig['password_encrypted']); - } - - /** - * Test read legacy passwords - */ - public function testReadLegacyPassword() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - $config = $this->readUserConfig(); - // simulate non-encrypted password situation - $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options']['password'] = 'smbpasswd'; - - $this->writeUserConfig($config); - - // re-read config, password was read correctly - $config = OC_Mount_Config::getPersonalMountPoints(); - $savedMountConfig = $config[0]['options']; - $this->assertEquals($mountConfig, $savedMountConfig); - } - - public function testVariableSubstitution() { - $legacyBackendOptions = [ - 'user' => 'someuser', - 'password' => 'somepassword', - 'replacethis' => '$user', - ]; - $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); - - $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', - 'options' => $legacyBackendOptions, - 'mountOptions' => ['preview' => false, 'int' => 1], - ]; - // different mount options - $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', - 'options' => $legacyBackendOptions, - 'mountOptions' => ['preview' => true, 'string' => 'abc'], - ]; - - $json = [ - 'user' => [ - self::TEST_USER1 => [ - '/$user/files/somemount' => $legacyConfig, - '/$user/files/anothermount' => $legacyConfig2, - ], - ], - ]; - - $this->writeGlobalConfig($json); - - // re-read config, password was read correctly - $config = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $config1 = $config['/' . self::TEST_USER1 . '/files/somemount']; - $config2 = $config['/' . self::TEST_USER1 . '/files/anothermount']; - - $this->assertSame(self::TEST_USER1, $config1['options']['replacethis']); - $this->assertSame(self::TEST_USER1, $config1['options']['replacethis']); - $this->assertSame(1, $config1['mountOptions']['int']); - $this->assertSame(true, $config2['mountOptions']['preview']); - $this->assertSame('abc', $config2['mountOptions']['string']); - } - - - public function mountDataProvider() { - return array( - // Tests for visible mount points - // system mount point for all users - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - 'all', - self::TEST_USER1, - true, - ), - // system mount point for a specific user - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER1, - true, - ), - // system mount point for a specific group - array( - false, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - self::TEST_USER1, - true, - ), - // user mount point - array( - true, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER1, - true, - ), - - // Tests for non-visible mount points - // system mount point for another user - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER2, - self::TEST_USER1, - false, - ), - // system mount point for a specific group - array( - false, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP2, - self::TEST_USER1, - false, - ), - // user mount point - array( - true, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER2, - false, - ), - ); - } - - /** - * Test mount points used at mount time, making sure - * the configuration is prepared properly. - * - * @dataProvider mountDataProvider - * @param bool $isPersonal true for personal mount point, false for system mount point - * @param string $mountType mount type - * @param string $applicable target user/group or "all" - * @param string $testUser user for which to retrieve the mount points - * @param bool $expectVisible whether to expect the mount point to be visible for $testUser - */ - public function testMount($isPersonal, $mountType, $applicable, $testUser, $expectVisible) { - - $mountConfig = array( - 'host' => 'someost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // add mount point as "test" user - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // check mount points in the perspective of user $testUser - \OC_User::setUserId($testUser); - - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints($testUser); - if ($expectVisible) { - $this->assertEquals(1, count($mountPoints)); - $this->assertTrue(isset($mountPoints['/' . self::TEST_USER1 . '/files/ext'])); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $mountPoints['/' . self::TEST_USER1 . '/files/ext']['class']); - $this->assertEquals($mountConfig, $mountPoints['/' . self::TEST_USER1 . '/files/ext']['options']); - } - else { - $this->assertEquals(0, count($mountPoints)); - } - } - - /** - * Test the same config for multiple users. - * The config will be merged by getSystemMountPoints(). - */ - public function testConfigMerging() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $isPersonal = false; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER2, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP2, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($options, $config[0]['options']); - $this->assertEquals(array(self::TEST_USER1, self::TEST_USER2), $config[0]['applicable']['users']); - $this->assertEquals(array(self::TEST_GROUP2, self::TEST_GROUP1), $config[0]['applicable']['groups']); - } - - /** - * Create then re-read mount points configs where the mount points - * have the same path, the config must NOT be merged. - */ - public function testRereadMountpointWithSamePath() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $isPersonal = false; - $options1 = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options1, - $mountType, - self::TEST_USER1, - $isPersonal - ) - ); - - $options2 = array( - 'host' => 'anothersmbhost', - 'user' => 'anothersmbuser', - 'password' => 'anothersmbpassword', - 'share' => 'anothersmbshare', - 'root' => 'anothersmbroot' - ); - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options2, - $mountType, - self::TEST_USER2, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(2, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($options1, $config[0]['options']); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[1]['class']); - $this->assertEquals('ext', $config[1]['mountpoint']); - $this->assertEquals($options2, $config[1]['options']); - } - - public function priorityDataProvider() { - return array( - - // test 1 - group vs group - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1, - 'priority' => 50 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1B, - 'priority' => 60 - ) - ), - 1 - ), - // test 2 - user vs personal - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => 2000 - ), - array( - 'isPersonal' => true, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => null - ) - ), - 1 - ), - // test 3 - all vs group vs user - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => 'all', - 'priority' => 70 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1, - 'priority' => 60 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => 50 - ) - ), - 2 - ) - - ); - } - - /** - * Ensure priorities are being respected - * Test user is self::TEST_USER1 - * - * @dataProvider priorityDataProvider - * @param array[] $mounts array of associative array of mount parameters: - * bool $isPersonal - * string $mountType - * string $applicable - * int|null $priority null for personal - * @param int $expected index of expected visible mount - */ - public function testPriority($mounts, $expected) { - - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // Add mount points - foreach($mounts as $i => $mount) { - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig + array('id' => $i), - $mount['mountType'], - $mount['applicable'], - $mount['isPersonal'], - $mount['priority'] - ) - ); - } - - // Get mount points for user - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $this->assertEquals(1, count($mountPoints)); - $this->assertEquals($expected, $mountPoints['/'.self::TEST_USER1.'/files/ext']['options']['id']); - } - - /** - * Test for persistence of priority when changing mount options - */ - public function testPriorityPersistence() { - - $class = 'Test_Mount_Config_Dummy_Storage'; - $priority = 123; - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $class, - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - false, - $priority - ) - ); - - // Check for correct priority - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - $this->assertEquals($priority, - $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); - - // Simulate changed mount options (without priority set) - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $class, - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - false - ) - ); - - // Check for correct priority - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - $this->assertEquals($priority, - $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); - } - - /* - * Test for correct personal configuration loading in file sharing scenarios - */ - public function testMultiUserPersonalConfigLoading() { - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // Create personal mount point - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - true - ) - ); - - // Ensure other user can read mount points - \OC_User::setUserId(self::TEST_USER2); - $mountPointsMe = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER2); - $mountPointsOther = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $this->assertEquals(0, count($mountPointsMe)); - $this->assertEquals(1, count($mountPointsOther)); - $this->assertTrue(isset($mountPointsOther['/'.self::TEST_USER1.'/files/ext'])); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', - $mountPointsOther['/'.self::TEST_USER1.'/files/ext']['class']); - $this->assertEquals($mountConfig, - $mountPointsOther['/'.self::TEST_USER1.'/files/ext']['options']); - } - - public function testAllowWritingIncompleteConfigIfStorageContructorFails() { - $storageClass = 'Test_Mount_Config_Dummy_Storage'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $storageClass, - array('simulateFail' => true), - $mountType, - $applicable, - $isPersonal - ) - ); - - // config can be retrieved afterwards - $mounts = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($mounts)); - - // no storage id was set - $this->assertFalse(isset($mounts[0]['storage_id'])); - } -} diff --git a/settings/personal.php b/settings/personal.php index 203c9f68af..bbbba3b5b8 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -108,10 +108,7 @@ $clients = array( $enableCertImport = false; $externalStorageEnabled = \OC::$server->getAppManager()->isEnabledForUser('files_external'); if ($externalStorageEnabled) { - $backends = OC_Mount_Config::getPersonalBackends(); - if (!empty($backends)) { - $enableCertImport = true; - } + $enableCertImport = true; } From c592e24c871f0f6a8d688c5c93e769b1505e4b6d Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 21:43:29 +0100 Subject: [PATCH 07/10] Make Application a singleton The same Application must be used in the settings templates and in routes, so that any registered backends are correctly seen --- apps/files_external/appinfo/app.php | 8 ++--- apps/files_external/appinfo/routes.php | 3 +- apps/files_external/lib/config.php | 31 +++++++------------ apps/files_external/personal.php | 4 +-- apps/files_external/settings.php | 4 +-- .../tests/service/storagesservicetest.php | 7 ++++- 6 files changed, 25 insertions(+), 32 deletions(-) diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 37a489535e..6c659af8aa 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -47,8 +47,9 @@ OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php'; require_once __DIR__ . '/../3rdparty/autoload.php'; -$app = new \OCA\Files_external\Appinfo\Application(); -$appContainer = $app->getContainer(); +// register Application object singleton +\OC_Mount_Config::$app = new \OCA\Files_external\Appinfo\Application(); +$appContainer = \OC_Mount_Config::$app->getContainer(); $l = \OC::$server->getL10N('files_external'); @@ -65,9 +66,6 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == ' "name" => $l->t('External storage') ]); -// Teach OC_Mount_Config about the AppFramework -\OC_Mount_Config::initApp($appContainer); - // connecting hooks OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook'); OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login'); diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php index bc4b0e98c9..213e7b28dc 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -28,8 +28,7 @@ namespace OCA\Files_External\AppInfo; /** * @var $this \OC\Route\Router **/ -$application = new Application(); -$application->registerRoutes( +\OC_Mount_Config::$app->registerRoutes( $this, array( 'resources' => array( diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 6fdf050fb6..5c38a3ba0c 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -32,7 +32,7 @@ */ use phpseclib\Crypt\AES; -use \OCP\AppFramework\IAppContainer; +use \OCA\Files_External\Appinfo\Application; use \OCA\Files_External\Lib\BackendConfig; use \OCA\Files_External\Service\BackendService; use \OCA\Files_External\Lib\Backend\LegacyBackend; @@ -56,17 +56,8 @@ class OC_Mount_Config { // whether to skip backend test (for unit tests, as this static class is not mockable) public static $skipTest = false; - /** @var IAppContainer */ - private static $appContainer; - - /** - * Teach OC_Mount_Config about the AppFramework - * - * @param IAppContainer $appContainer - */ - public static function initApp(IAppContainer $appContainer) { - self::$appContainer = $appContainer; - } + /** @var Application */ + public static $app; /** * @param string $class @@ -75,8 +66,8 @@ class OC_Mount_Config { * @deprecated 8.2.0 use \OCA\Files_External\Service\BackendService::registerBackend() */ public static function registerBackend($class, $definition) { - $backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService'); - $auth = self::$appContainer->query('OCA\Files_External\Lib\Auth\Builtin'); + $backendService = self::$app->getContainer()->query('OCA\Files_External\Service\BackendService'); + $auth = self::$app->getContainer()->query('OCA\Files_External\Lib\Auth\Builtin'); $backendService->registerBackend(new LegacyBackend($class, $definition, $auth)); @@ -128,9 +119,9 @@ class OC_Mount_Config { public static function getAbsoluteMountPoints($uid) { $mountPoints = array(); - $userGlobalStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserGlobalStoragesService'); - $userStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserStoragesService'); - $user = self::$appContainer->query('OCP\IUserManager')->get($uid); + $userGlobalStoragesService = self::$app->getContainer()->query('OCA\Files_External\Service\UserGlobalStoragesService'); + $userStoragesService = self::$app->getContainer()->query('OCA\Files_External\Service\UserStoragesService'); + $user = self::$app->getContainer()->query('OCP\IUserManager')->get($uid); $userGlobalStoragesService->setUser($user); $userStoragesService->setUser($user); @@ -168,7 +159,7 @@ class OC_Mount_Config { */ public static function getSystemMountPoints() { $mountPoints = []; - $service = self::$appContainer->query('OCA\Files_External\Service\GlobalStoragesService'); + $service = self::$app->getContainer()->query('OCA\Files_External\Service\GlobalStoragesService'); foreach ($service->getAllStorages() as $storage) { $mountPoints[] = self::prepareMountPointEntry($storage, false); @@ -184,7 +175,7 @@ class OC_Mount_Config { */ public static function getPersonalMountPoints() { $mountPoints = []; - $service = self::$appContainer->query('OCA\Files_External\Service\UserStoragesService'); + $service = self::$app->getContainer()->query('OCA\Files_External\Service\UserStoragesService'); foreach ($service->getAllStorages() as $storage) { $mountPoints[] = self::prepareMountPointEntry($storage, true); @@ -533,7 +524,7 @@ class OC_Mount_Config { return false; } - $service = self::$appContainer->query('OCA\Files_External\Service\BackendService'); + $service = self::$app->getContainer()->query('OCA\Files_External\Service\BackendService'); $class = $service->getBackend($options['backend'])->getStorageClass(); try { /** @var \OC\Files\Storage\Storage $storage */ diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index fec1c195bb..696ca51b2e 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -26,8 +26,8 @@ use \OCA\Files_External\Service\BackendService; -$app = new \OCA\Files_external\Appinfo\Application(); -$appContainer = $app->getContainer(); +// we must use the same container +$appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService'); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 7e20af0c60..2ad31a3113 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -30,8 +30,8 @@ use \OCA\Files_External\Service\BackendService; OC_Util::checkAdminUser(); -$app = new \OCA\Files_external\Appinfo\Application(); -$appContainer = $app->getContainer(); +// we must use the same container +$appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService'); diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 07286106c7..28220c9bc2 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -121,7 +121,12 @@ abstract class StoragesServiceTest extends \Test\TestCase { return $this->backendService; } })); - \OC_Mount_Config::initApp($containerMock); + + \OC_Mount_Config::$app = $this->getMockBuilder('\OCA\Files_External\Appinfo\Application') + ->disableOriginalConstructor() + ->getMock(); + \OC_Mount_Config::$app->method('getContainer') + ->willReturn($containerMock); } public function tearDown() { From b6eb952ac61326a15108820b0dd0a1712f00dfdb Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 12 Aug 2015 19:51:09 +0100 Subject: [PATCH 08/10] Propagate auth mechanism/backend failures to filesystem layer Failure to prepare the storage during backend or auth mechanism manipulation will throw an InsufficientDataForMeaningfulAnswerException, which is propagated to StorageNotAvailableException in the filesystem layer via the FailedStorage helper class. When a storage is unavailable not due to failure, but due to insufficient data being available, a special 'indeterminate' status is returned to the configuration UI. --- .../controller/storagescontroller.php | 36 ++-- apps/files_external/js/settings.js | 8 +- apps/files_external/lib/config.php | 3 +- .../lib/config/configadapter.php | 29 ++- apps/files_external/lib/failedstorage.php | 200 ++++++++++++++++++ ...icientdataformeaningfulanswerexception.php | 30 +++ .../lib/storagemodifiertrait.php | 6 + settings/css/settings.css | 12 +- 8 files changed, 297 insertions(+), 27 deletions(-) create mode 100644 apps/files_external/lib/failedstorage.php create mode 100644 apps/files_external/lib/insufficientdataformeaningfulanswerexception.php diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index f1d1625bdc..3d91af8bd8 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -34,6 +34,8 @@ use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_External\Lib\Backend\Backend; use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCP\Files\StorageNotAvailableException; +use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; /** * Base class for storages controllers @@ -182,21 +184,27 @@ abstract class StoragesController extends Controller { * @param StorageConfig $storage storage configuration */ protected function updateStorageStatus(StorageConfig &$storage) { - /** @var AuthMechanism */ - $authMechanism = $storage->getAuthMechanism(); - $authMechanism->manipulateStorageConfig($storage); - /** @var Backend */ - $backend = $storage->getBackend(); - $backend->manipulateStorageConfig($storage); + try { + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + $authMechanism->manipulateStorageConfig($storage); + /** @var Backend */ + $backend = $storage->getBackend(); + $backend->manipulateStorageConfig($storage); - // update status (can be time-consuming) - $storage->setStatus( - \OC_Mount_Config::getBackendStatus( - $storage->getBackend()->getStorageClass(), - $storage->getBackendOptions(), - false - ) - ); + // update status (can be time-consuming) + $storage->setStatus( + \OC_Mount_Config::getBackendStatus( + $backend->getStorageClass(), + $storage->getBackendOptions(), + false + ) + ); + } catch (InsufficientDataForMeaningfulAnswerException $e) { + $storage->setStatus(\OC_Mount_Config::STATUS_INDETERMINATE); + } catch (StorageNotAvailableException $e) { + $storage->setStatus(\OC_Mount_Config::STATUS_ERROR); + } } /** diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 7288f90fa7..c15f36d8bd 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -191,7 +191,8 @@ var StorageConfig = function(id) { StorageConfig.Status = { IN_PROGRESS: -1, SUCCESS: 0, - ERROR: 1 + ERROR: 1, + INDETERMINATE: 2 }; /** * @memberof OCA.External.Settings @@ -946,7 +947,7 @@ MountConfigListView.prototype = { */ updateStatus: function($tr, status) { var $statusSpan = $tr.find('.status span'); - $statusSpan.removeClass('success error loading-small'); + $statusSpan.removeClass('loading-small success indeterminate error'); switch (status) { case StorageConfig.Status.IN_PROGRESS: $statusSpan.addClass('loading-small'); @@ -954,6 +955,9 @@ MountConfigListView.prototype = { case StorageConfig.Status.SUCCESS: $statusSpan.addClass('success'); break; + case StorageConfig.Status.INDETERMINATE: + $statusSpan.addClass('indeterminate'); + break; default: $statusSpan.addClass('error'); } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 5c38a3ba0c..91c33ef10a 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -52,6 +52,7 @@ class OC_Mount_Config { // getBackendStatus return types const STATUS_SUCCESS = 0; const STATUS_ERROR = 1; + const STATUS_INDETERMINATE = 2; // whether to skip backend test (for unit tests, as this static class is not mockable) public static $skipTest = false; @@ -218,7 +219,7 @@ class OC_Mount_Config { * @param string|array $input * @return string */ - private static function setUserVars($user, $input) { + public static function setUserVars($user, $input) { if (is_array($input)) { foreach ($input as &$value) { if (is_string($value)) { diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index 9829629761..a15d9e06a5 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -32,6 +32,8 @@ use OCP\IUser; use OCA\Files_external\Service\UserStoragesService; use OCA\Files_External\Service\UserGlobalStoragesService; use OCA\Files_External\Lib\StorageConfig; +use OCP\Files\StorageNotAvailableException; +use OCA\Files_External\Lib\FailedStorage; /** * Make the old files_external config work with the new public mount config api @@ -60,8 +62,15 @@ class ConfigAdapter implements IMountProvider { * Process storage ready for mounting * * @param StorageConfig $storage + * @param IUser $user */ - private function prepareStorageConfig(StorageConfig &$storage) { + private function prepareStorageConfig(StorageConfig &$storage, IUser $user) { + foreach ($storage->getBackendOptions() as $option => $value) { + $storage->setBackendOption($option, \OC_Mount_Config::setUserVars( + $user->getUID(), $value + )); + } + $objectStore = $storage->getBackendOption('objectstore'); if ($objectStore) { $objectClass = $objectStore['class']; @@ -103,8 +112,13 @@ class ConfigAdapter implements IMountProvider { $this->userGlobalStoragesService->setUser($user); foreach ($this->userGlobalStoragesService->getAllStorages() as $storage) { - $this->prepareStorageConfig($storage); - $impl = $this->constructStorage($storage); + try { + $this->prepareStorageConfig($storage, $user); + $impl = $this->constructStorage($storage); + } catch (\Exception $e) { + // propagate exception into filesystem + $impl = new FailedStorage(['exception' => $e]); + } $mount = new MountPoint( $impl, @@ -117,8 +131,13 @@ class ConfigAdapter implements IMountProvider { } foreach ($this->userStoragesService->getAllStorages() as $storage) { - $this->prepareStorageConfig($storage); - $impl = $this->constructStorage($storage); + try { + $this->prepareStorageConfig($storage, $user); + $impl = $this->constructStorage($storage); + } catch (\Exception $e) { + // propagate exception into filesystem + $impl = new FailedStorage(['exception' => $e]); + } $mount = new PersonalMount( $this->userStoragesService, diff --git a/apps/files_external/lib/failedstorage.php b/apps/files_external/lib/failedstorage.php new file mode 100644 index 0000000000..6afa98052c --- /dev/null +++ b/apps/files_external/lib/failedstorage.php @@ -0,0 +1,200 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Lock\ILockingProvider; +use \OC\Files\Storage\Common; +use \OCP\Files\StorageNotAvailableException; + +/** + * Storage placeholder to represent a missing precondition, storage unavailable + */ +class FailedStorage extends Common { + + /** @var \Exception */ + protected $e; + + /** + * @param array $params ['exception' => \Exception] + */ + public function __construct($params) { + $this->e = $params['exception']; + } + + public function getId() { + // we can't return anything sane here + return 'failedstorage'; + } + + public function mkdir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function rmdir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function opendir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function is_dir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function is_file($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function stat($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filetype($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filesize($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isCreatable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isReadable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isUpdatable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isDeletable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isSharable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getPermissions($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_exists($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filemtime($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_get_contents($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_put_contents($path, $data) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function unlink($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function rename($path1, $path2) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function copy($path1, $path2) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function fopen($path, $mode) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getMimeType($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function hash($type, $path, $raw = false) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function free_space($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function search($query) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function touch($path, $mtime = null) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getLocalFile($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getLocalFolder($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function hasUpdated($path, $time) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getETag($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getDirectDownload($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function verifyPath($path, $fileName) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function acquireLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function releaseLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function changeLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + +} diff --git a/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php new file mode 100644 index 0000000000..dd4cd75df1 --- /dev/null +++ b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php @@ -0,0 +1,30 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Files\StorageNotAvailableException; + +/** + * Authentication mechanism or backend has insufficient data + */ +class InsufficientDataForMeaningfulAnswerException extends StorageNotAvailableException { +} diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php index a11d265e84..3af0bb234d 100644 --- a/apps/files_external/lib/storagemodifiertrait.php +++ b/apps/files_external/lib/storagemodifiertrait.php @@ -23,6 +23,8 @@ namespace OCA\Files_External\Lib; use \OCP\Files\Storage; use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; +use \OCP\Files\StorageNotAvailableException; /** * Trait for objects that can modify StorageConfigs and wrap Storages @@ -43,6 +45,8 @@ trait StorageModifierTrait { * Modify a StorageConfig parameters * * @param StorageConfig $storage + * @throws InsufficientDataForMeaningfulAnswerException + * @throws StorageNotAvailableException */ public function manipulateStorageConfig(StorageConfig &$storage) { } @@ -52,6 +56,8 @@ trait StorageModifierTrait { * * @param Storage $storage * @return Storage + * @throws InsufficientDataForMeaningfulAnswerException + * @throws StorageNotAvailableException */ public function wrapStorage(Storage $storage) { return $storage; diff --git a/settings/css/settings.css b/settings/css/settings.css index 7340f2b2a6..9bd342ca6f 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -431,14 +431,16 @@ table.grid td.date{ } span.success { - background: #37ce02; - border-radius: 3px; + background: #37ce02; + border-radius: 3px; } - span.error { - background: #ce3702; + background: #ce3702; +} +span.indeterminate { + background: #e6db00; + border-radius: 40% 0; } - /* PASSWORD */ .strengthify-wrapper { From df8f5425dc32a98f2c5c656215fa0e8564d6a282 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Tue, 11 Aug 2015 19:18:36 +0100 Subject: [PATCH 09/10] Load custom JS for all backends/auth mechanisms Loading custom JS on a per-backend basis added needless complexity and made dealing with async required. Now all backends/auth mechanisms load custom JS in PHP --- apps/files_external/js/settings.js | 3 --- apps/files_external/personal.php | 17 +++++++++++++++-- apps/files_external/settings.php | 17 +++++++++++++++-- apps/files_external/templates/settings.php | 9 --------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index c15f36d8bd..6bf0143f1c 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -731,9 +731,6 @@ MountConfigListView.prototype = { var priorityEl = $(''); $tr.append(priorityEl); - if (backendConfiguration['custom'] && $el.find('tbody tr.'+backend.replace(/\\/g, '\\\\')).length === 1) { - OC.addScript('files_external', backendConfiguration['custom']); - } $td.children().not('[type=hidden]').first().focus(); $tr.find('td').last().attr('class', 'remove'); diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 696ca51b2e..8717d91d4f 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -34,11 +34,24 @@ $userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStor OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); +$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL); +$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_PERSONAL); +foreach ($backends as $backend) { + if ($backend->getCustomJs()) { + \OCP\Util::addScript('files_external', $backend->getCustomJs()); + } +} +foreach ($authMechanisms as $authMechanism) { + if ($authMechanism->getCustomJs()) { + \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); + } +} + $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', false); $tmpl->assign('storages', $userStoragesService->getAllStorages()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); -$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL)); -$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); +$tmpl->assign('backends', $backends); +$tmpl->assign('authMechanisms', $authMechanisms); return $tmpl->fetchPage(); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 2ad31a3113..9cecc0c6a4 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -41,12 +41,25 @@ OCP\Util::addStyle('files_external', 'settings'); \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); +$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN); +$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_ADMIN); +foreach ($backends as $backend) { + if ($backend->getCustomJs()) { + \OCP\Util::addScript('files_external', $backend->getCustomJs()); + } +} +foreach ($authMechanisms as $authMechanism) { + if ($authMechanism->getCustomJs()) { + \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); + } +} + $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', true); $tmpl->assign('storages', $globalStoragesService->getAllStorages()); -$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN)); -$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); +$tmpl->assign('backends', $backends); +$tmpl->assign('authMechanisms', $authMechanisms); $tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL)); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 589574bbae..611c5807a5 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -106,15 +106,6 @@ foreach ($storage->getAuthMechanism()->getParameters() as $parameter) { writeParameterInput($parameter, $options, ['auth-param']); } - - $customJs = $storage->getBackend()->getCustomJs(); - if (isset($customJs)) { - \OCP\Util::addScript('files_external', $customJs); - } - $customJsAuth = $storage->getAuthMechanism()->getCustomJs(); - if (isset($customJsAuth)) { - \OCP\Util::addScript('files_external', $customJsAuth); - } ?> From eb0e5ce12c5200dbe29d51d8bfd561e4df6267b5 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Wed, 19 Aug 2015 10:04:42 +0100 Subject: [PATCH 10/10] Restrict Local backend to admin-only --- apps/files_external/appinfo/app.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 6c659af8aa..4bdfd31679 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -77,6 +77,10 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\Local', [ 'datadir' => (string)$l->t('Location') ], ]); +// Local must only be visible to the admin +$appContainer->query('OCA\Files_External\Service\BackendService') + ->getBackend('\OC\Files\Storage\Local') + ->setAllowedVisibility(\OCA\Files_External\Service\BackendService::VISIBILITY_ADMIN); OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', [ 'backend' => (string)$l->t('Amazon S3'),