aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_external/lib
diff options
context:
space:
mode:
authorJoas Schilling <nickvergessen@owncloud.com>2016-05-13 11:22:28 +0200
committerJoas Schilling <nickvergessen@owncloud.com>2016-05-24 08:41:20 +0200
commit328b3c47d7fdf61b6de3c2d9129d392d3a9f5a87 (patch)
tree4d7f5eedc7861b40271bcfe1e95cc01d5ed0296d /apps/files_external/lib
parentadcf942901fd567d97dbe105e8f3dfb7cea738e3 (diff)
downloadnextcloud-server-328b3c47d7fdf61b6de3c2d9129d392d3a9f5a87.tar.gz
nextcloud-server-328b3c47d7fdf61b6de3c2d9129d392d3a9f5a87.zip
Move stuff from outside lib/ to PSR-4
Diffstat (limited to 'apps/files_external/lib')
-rw-r--r--apps/files_external/lib/AppInfo/Application.php135
-rw-r--r--apps/files_external/lib/Command/Applicable.php154
-rw-r--r--apps/files_external/lib/Command/Backends.php112
-rw-r--r--apps/files_external/lib/Command/Config.php120
-rw-r--r--apps/files_external/lib/Command/Create.php221
-rw-r--r--apps/files_external/lib/Command/Delete.php110
-rw-r--r--apps/files_external/lib/Command/Export.php56
-rw-r--r--apps/files_external/lib/Command/Import.php223
-rw-r--r--apps/files_external/lib/Command/ListCommand.php247
-rw-r--r--apps/files_external/lib/Command/Option.php75
-rw-r--r--apps/files_external/lib/Command/Verify.php142
-rw-r--r--apps/files_external/lib/Controller/AjaxController.php64
-rw-r--r--apps/files_external/lib/Controller/GlobalStoragesController.php184
-rw-r--r--apps/files_external/lib/Controller/StoragesController.php338
-rw-r--r--apps/files_external/lib/Controller/UserGlobalStoragesController.php201
-rw-r--r--apps/files_external/lib/Controller/UserStoragesController.php222
-rw-r--r--apps/files_external/lib/Migration/DummyUserSession.php51
-rw-r--r--apps/files_external/lib/Migration/StorageMigrator.php143
-rw-r--r--apps/files_external/lib/Service/BackendService.php282
-rw-r--r--apps/files_external/lib/Service/DBConfigService.php451
-rw-r--r--apps/files_external/lib/Service/GlobalLegacyStoragesService.php44
-rw-r--r--apps/files_external/lib/Service/GlobalStoragesService.php165
-rw-r--r--apps/files_external/lib/Service/ImportLegacyStoragesService.php46
-rw-r--r--apps/files_external/lib/Service/LegacyStoragesService.php209
-rw-r--r--apps/files_external/lib/Service/StoragesService.php527
-rw-r--r--apps/files_external/lib/Service/UserGlobalStoragesService.php174
-rw-r--r--apps/files_external/lib/Service/UserLegacyStoragesService.php54
-rw-r--r--apps/files_external/lib/Service/UserStoragesService.php141
-rw-r--r--apps/files_external/lib/Service/UserTrait.php74
-rw-r--r--apps/files_external/lib/config/configadapter.php4
30 files changed, 4967 insertions, 2 deletions
diff --git a/apps/files_external/lib/AppInfo/Application.php b/apps/files_external/lib/AppInfo/Application.php
new file mode 100644
index 00000000000..d7c650526b3
--- /dev/null
+++ b/apps/files_external/lib/AppInfo/Application.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Ross Nicoll <jrn@jrn.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\AppInfo;
+
+use \OCP\AppFramework\App;
+use OCP\AppFramework\IAppContainer;
+use \OCP\IContainer;
+use \OCA\Files_External\Service\BackendService;
+use \OCA\Files_External\Lib\Config\IBackendProvider;
+use \OCA\Files_External\Lib\Config\IAuthMechanismProvider;
+
+/**
+ * @package OCA\Files_External\AppInfo
+ */
+class Application extends App implements IBackendProvider, IAuthMechanismProvider {
+
+ public function __construct(array $urlParams = array()) {
+ parent::__construct('files_external', $urlParams);
+
+ $container = $this->getContainer();
+
+ $container->registerService('OCP\Files\Config\IUserMountCache', function (IAppContainer $c) {
+ return $c->getServer()->query('UserMountCache');
+ });
+
+ $backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
+ $backendService->registerBackendProvider($this);
+ $backendService->registerAuthMechanismProvider($this);
+
+ // app developers: do NOT depend on this! it will disappear with oC 9.0!
+ \OC::$server->getEventDispatcher()->dispatch(
+ 'OCA\\Files_External::loadAdditionalBackends'
+ );
+ }
+
+ /**
+ * Register settings templates
+ */
+ public function registerSettings() {
+ $container = $this->getContainer();
+ $backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
+
+ \OCP\App::registerAdmin('files_external', 'settings');
+ \OCP\App::registerPersonal('files_external', 'personal');
+ }
+
+ /**
+ * @{inheritdoc}
+ */
+ public function getBackends() {
+ $container = $this->getContainer();
+
+ $backends = [
+ $container->query('OCA\Files_External\Lib\Backend\Local'),
+ $container->query('OCA\Files_External\Lib\Backend\FTP'),
+ $container->query('OCA\Files_External\Lib\Backend\DAV'),
+ $container->query('OCA\Files_External\Lib\Backend\OwnCloud'),
+ $container->query('OCA\Files_External\Lib\Backend\SFTP'),
+ $container->query('OCA\Files_External\Lib\Backend\AmazonS3'),
+ $container->query('OCA\Files_External\Lib\Backend\Dropbox'),
+ $container->query('OCA\Files_External\Lib\Backend\Google'),
+ $container->query('OCA\Files_External\Lib\Backend\Swift'),
+ $container->query('OCA\Files_External\Lib\Backend\SFTP_Key'),
+ ];
+
+ if (!\OC_Util::runningOnWindows()) {
+ $backends += [
+ $container->query('OCA\Files_External\Lib\Backend\SMB'),
+ $container->query('OCA\Files_External\Lib\Backend\SMB_OC'),
+ ];
+ }
+
+ return $backends;
+ }
+
+ /**
+ * @{inheritdoc}
+ */
+ public function getAuthMechanisms() {
+ $container = $this->getContainer();
+
+ return [
+ // AuthMechanism::SCHEME_NULL mechanism
+ $container->query('OCA\Files_External\Lib\Auth\NullMechanism'),
+
+ // AuthMechanism::SCHEME_BUILTIN mechanism
+ $container->query('OCA\Files_External\Lib\Auth\Builtin'),
+
+ // AuthMechanism::SCHEME_PASSWORD mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\Password\Password'),
+ $container->query('OCA\Files_External\Lib\Auth\Password\SessionCredentials'),
+
+ // AuthMechanism::SCHEME_OAUTH1 mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\OAuth1\OAuth1'),
+
+ // AuthMechanism::SCHEME_OAUTH2 mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\OAuth2\OAuth2'),
+
+ // AuthMechanism::SCHEME_PUBLICKEY mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\PublicKey\RSA'),
+
+ // AuthMechanism::SCHEME_OPENSTACK mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\OpenStack\OpenStack'),
+ $container->query('OCA\Files_External\Lib\Auth\OpenStack\Rackspace'),
+
+ // Specialized mechanisms
+ $container->query('OCA\Files_External\Lib\Auth\AmazonS3\AccessKey'),
+ ];
+ }
+
+}
diff --git a/apps/files_external/lib/Command/Applicable.php b/apps/files_external/lib/Command/Applicable.php
new file mode 100644
index 00000000000..902e76cb4cb
--- /dev/null
+++ b/apps/files_external/lib/Command/Applicable.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_external\NotFoundException;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Applicable extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ protected $globalService;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ function __construct(
+ GlobalStoragesService $globalService,
+ IUserManager $userManager,
+ IGroupManager $groupManager
+ ) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:applicable')
+ ->setDescription('Manage applicable users and groups for a mount')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'The id of the mount to edit'
+ )->addOption(
+ 'add-user',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'user to add as applicable'
+ )->addOption(
+ 'remove-user',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'user to remove as applicable'
+ )->addOption(
+ 'add-group',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'group to add as applicable'
+ )->addOption(
+ 'remove-group',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'group to remove as applicable'
+ )->addOption(
+ 'remove-all',
+ null,
+ InputOption::VALUE_NONE,
+ 'Set the mount to be globally applicable'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mountId = $input->getArgument('mount_id');
+ try {
+ $mount = $this->globalService->getStorage($mountId);
+ } catch (NotFoundException $e) {
+ $output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts</error>');
+ return 404;
+ }
+
+ if ($mount->getType() === StorageConfig::MOUNT_TYPE_PERSONAl) {
+ $output->writeln('<error>Can\'t change applicables on personal mounts</error>');
+ return 1;
+ }
+
+ $addUsers = $input->getOption('add-user');
+ $removeUsers = $input->getOption('remove-user');
+ $addGroups = $input->getOption('add-group');
+ $removeGroups = $input->getOption('remove-group');
+
+ $applicableUsers = $mount->getApplicableUsers();
+ $applicableGroups = $mount->getApplicableGroups();
+
+ if ((count($addUsers) + count($removeUsers) + count($addGroups) + count($removeGroups) > 0) || $input->getOption('remove-all')) {
+ foreach ($addUsers as $addUser) {
+ if (!$this->userManager->userExists($addUser)) {
+ $output->writeln('<error>User "' . $addUser . '" not found</error>');
+ return 404;
+ }
+ }
+ foreach ($addGroups as $addGroup) {
+ if (!$this->groupManager->groupExists($addGroup)) {
+ $output->writeln('<error>Group "' . $addGroup . '" not found</error>');
+ return 404;
+ }
+ }
+
+ if ($input->getOption('remove-all')) {
+ $applicableUsers = [];
+ $applicableGroups = [];
+ } else {
+ $applicableUsers = array_unique(array_merge($applicableUsers, $addUsers));
+ $applicableUsers = array_values(array_diff($applicableUsers, $removeUsers));
+ $applicableGroups = array_unique(array_merge($applicableGroups, $addGroups));
+ $applicableGroups = array_values(array_diff($applicableGroups, $removeGroups));
+ }
+ $mount->setApplicableUsers($applicableUsers);
+ $mount->setApplicableGroups($applicableGroups);
+ $this->globalService->updateStorage($mount);
+ }
+
+ $this->writeArrayInOutputFormat($input, $output, [
+ 'users' => $applicableUsers,
+ 'groups' => $applicableGroups
+ ]);
+ }
+}
diff --git a/apps/files_external/lib/Command/Backends.php b/apps/files_external/lib/Command/Backends.php
new file mode 100644
index 00000000000..260ea210397
--- /dev/null
+++ b/apps/files_external/lib/Command/Backends.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCA\Files_External\Lib\Backend\Backend;
+use OCA\Files_External\Lib\DefinitionParameter;
+use OCA\Files_External\Service\BackendService;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Helper\TableHelper;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\Input;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Backends extends Base {
+ /** @var BackendService */
+ private $backendService;
+
+ function __construct(BackendService $backendService
+ ) {
+ parent::__construct();
+
+ $this->backendService = $backendService;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:backends')
+ ->setDescription('Show available authentication and storage backends')
+ ->addArgument(
+ 'type',
+ InputArgument::OPTIONAL,
+ 'only show backends of a certain type. Possible values are "authentication" or "storage"'
+ )->addArgument(
+ 'backend',
+ InputArgument::OPTIONAL,
+ 'only show information of a specific backend'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $authBackends = $this->backendService->getAuthMechanisms();
+ $storageBackends = $this->backendService->getBackends();
+
+ $data = [
+ 'authentication' => array_map([$this, 'serializeAuthBackend'], $authBackends),
+ 'storage' => array_map([$this, 'serializeAuthBackend'], $storageBackends)
+ ];
+
+ $type = $input->getArgument('type');
+ $backend = $input->getArgument('backend');
+ if ($type) {
+ if (!isset($data[$type])) {
+ $output->writeln('<error>Invalid type "' . $type . '". Possible values are "authentication" or "storage"</error>');
+ return 1;
+ }
+ $data = $data[$type];
+
+ if ($backend) {
+ if (!isset($data[$backend])) {
+ $output->writeln('<error>Unknown backend "' . $backend . '" of type "' . $type . '"</error>');
+ return 1;
+ }
+ $data = $data[$backend];
+ }
+ }
+
+ $this->writeArrayInOutputFormat($input, $output, $data);
+ }
+
+ private function serializeAuthBackend(\JsonSerializable $backend) {
+ $data = $backend->jsonSerialize();
+ $result = [
+ 'name' => $data['name'],
+ 'identifier' => $data['identifier'],
+ 'configuration' => array_map(function (DefinitionParameter $parameter) {
+ return $parameter->getTypeName();
+ }, $data['configuration'])
+ ];
+ if ($backend instanceof Backend) {
+ $result['storage_class'] = $backend->getStorageClass();
+ $authBackends = $this->backendService->getAuthMechanismsByScheme(array_keys($backend->getAuthSchemes()));
+ $result['supported_authentication_backends'] = array_keys($authBackends);
+ }
+ return $result;
+ }
+}
diff --git a/apps/files_external/lib/Command/Config.php b/apps/files_external/lib/Command/Config.php
new file mode 100644
index 00000000000..873fece86d7
--- /dev/null
+++ b/apps/files_external/lib/Command/Config.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_external\NotFoundException;
+use OCA\Files_External\Service\GlobalStoragesService;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Helper\TableHelper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Config extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ protected $globalService;
+
+ function __construct(GlobalStoragesService $globalService) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:config')
+ ->setDescription('Manage backend configuration for a mount')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'The id of the mount to edit'
+ )->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ 'key of the config option to set/get'
+ )->addArgument(
+ 'value',
+ InputArgument::OPTIONAL,
+ 'value to set the config option to, when no value is provided the existing value will be printed'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mountId = $input->getArgument('mount_id');
+ $key = $input->getArgument('key');
+ try {
+ $mount = $this->globalService->getStorage($mountId);
+ } catch (NotFoundException $e) {
+ $output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
+ return 404;
+ }
+
+ $value = $input->getArgument('value');
+ if ($value) {
+ $this->setOption($mount, $key, $value, $output);
+ } else {
+ $this->getOption($mount, $key, $output);
+ }
+ }
+
+ /**
+ * @param StorageConfig $mount
+ * @param string $key
+ * @param OutputInterface $output
+ */
+ protected function getOption(StorageConfig $mount, $key, OutputInterface $output) {
+ if ($key === 'mountpoint' || $key === 'mount_point') {
+ $value = $mount->getMountPoint();
+ } else {
+ $value = $mount->getBackendOption($key);
+ }
+ if (!is_string($value)) { // show bools and objects correctly
+ $value = json_encode($value);
+ }
+ $output->writeln($value);
+ }
+
+ /**
+ * @param StorageConfig $mount
+ * @param string $key
+ * @param string $value
+ * @param OutputInterface $output
+ */
+ protected function setOption(StorageConfig $mount, $key, $value, OutputInterface $output) {
+ $decoded = json_decode($value, true);
+ if (!is_null($decoded)) {
+ $value = $decoded;
+ }
+ if ($key === 'mountpoint' || $key === 'mount_point') {
+ $mount->setMountPoint($value);
+ } else {
+ $mount->setBackendOption($key, $value);
+ }
+ $this->globalService->updateStorage($mount);
+ }
+}
diff --git a/apps/files_external/lib/Command/Create.php b/apps/files_external/lib/Command/Create.php
new file mode 100644
index 00000000000..17ad7ef1a16
--- /dev/null
+++ b/apps/files_external/lib/Command/Create.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OC\Files\Filesystem;
+use OC\User\NoUserException;
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCA\Files_External\Lib\Backend\Backend;
+use OCA\Files_External\Lib\DefinitionParameter;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_External\Service\BackendService;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCA\Files_External\Service\UserStoragesService;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Create extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ private $globalService;
+
+ /**
+ * @var UserStoragesService
+ */
+ private $userService;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /** @var BackendService */
+ private $backendService;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ function __construct(GlobalStoragesService $globalService,
+ UserStoragesService $userService,
+ IUserManager $userManager,
+ IUserSession $userSession,
+ BackendService $backendService
+ ) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->userService = $userService;
+ $this->userManager = $userManager;
+ $this->userSession = $userSession;
+ $this->backendService = $backendService;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:create')
+ ->setDescription('Create a new mount configuration')
+ ->addOption(
+ 'user',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'user to add the mount configuration for, if not set the mount will be added as system mount'
+ )
+ ->addArgument(
+ 'mount_point',
+ InputArgument::REQUIRED,
+ 'mount point for the new mount'
+ )
+ ->addArgument(
+ 'storage_backend',
+ InputArgument::REQUIRED,
+ 'storage backend identifier for the new mount, see `occ files_external:backends` for possible values'
+ )
+ ->addArgument(
+ 'authentication_backend',
+ InputArgument::REQUIRED,
+ 'authentication backend identifier for the new mount, see `occ files_external:backends` for possible values'
+ )
+ ->addOption(
+ 'config',
+ 'c',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Mount configuration option in key=value format'
+ )
+ ->addOption(
+ 'dry',
+ null,
+ InputOption::VALUE_NONE,
+ 'Don\'t save the created mount, only list the new mount'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $user = $input->getOption('user');
+ $mountPoint = $input->getArgument('mount_point');
+ $storageIdentifier = $input->getArgument('storage_backend');
+ $authIdentifier = $input->getArgument('authentication_backend');
+ $configInput = $input->getOption('config');
+
+ $storageBackend = $this->backendService->getBackend($storageIdentifier);
+ $authBackend = $this->backendService->getAuthMechanism($authIdentifier);
+
+ if (!Filesystem::isValidPath($mountPoint)) {
+ $output->writeln('<error>Invalid mountpoint "' . $mountPoint . '"</error>');
+ return 1;
+ }
+ if (is_null($storageBackend)) {
+ $output->writeln('<error>Storage backend with identifier "' . $storageIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>');
+ return 404;
+ }
+ if (is_null($authBackend)) {
+ $output->writeln('<error>Authentication backend with identifier "' . $authIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>');
+ return 404;
+ }
+ $supportedSchemes = array_keys($storageBackend->getAuthSchemes());
+ if (!in_array($authBackend->getScheme(), $supportedSchemes)) {
+ $output->writeln('<error>Authentication backend "' . $authIdentifier . '" not valid for storage backend "' . $storageIdentifier . '" (see `occ files_external:backends storage ' . $storageIdentifier . '` for possible values)</error>');
+ return 1;
+ }
+
+ $config = [];
+ foreach ($configInput as $configOption) {
+ if (!strpos($configOption, '=')) {
+ $output->writeln('<error>Invalid mount configuration option "' . $configOption . '"</error>');
+ return 1;
+ }
+ list($key, $value) = explode('=', $configOption, 2);
+ if (!$this->validateParam($key, $value, $storageBackend, $authBackend)) {
+ $output->writeln('<error>Unknown configuration for backends "' . $key . '"</error>');
+ return 1;
+ }
+ $config[$key] = $value;
+ }
+
+ $mount = new StorageConfig();
+ $mount->setMountPoint($mountPoint);
+ $mount->setBackend($storageBackend);
+ $mount->setAuthMechanism($authBackend);
+ $mount->setBackendOptions($config);
+
+ if ($user) {
+ if (!$this->userManager->userExists($user)) {
+ $output->writeln('<error>User "' . $user . '" not found</error>');
+ return 1;
+ }
+ $mount->setApplicableUsers([$user]);
+ }
+
+ if ($input->getOption('dry')) {
+ $this->showMount($user, $mount, $input, $output);
+ } else {
+ $this->getStorageService($user)->addStorage($mount);
+ if ($input->getOption('output') === self::OUTPUT_FORMAT_PLAIN) {
+ $output->writeln('<info>Storage created with id ' . $mount->getId() . '</info>');
+ } else {
+ $output->writeln($mount->getId());
+ }
+ }
+ return 0;
+ }
+
+ private function validateParam($key, &$value, Backend $storageBackend, AuthMechanism $authBackend) {
+ $params = array_merge($storageBackend->getParameters(), $authBackend->getParameters());
+ foreach ($params as $param) {
+ /** @var DefinitionParameter $param */
+ if ($param->getName() === $key) {
+ if ($param->getType() === DefinitionParameter::VALUE_BOOLEAN) {
+ $value = ($value === 'true');
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function showMount($user, StorageConfig $mount, InputInterface $input, OutputInterface $output) {
+ $listCommand = new ListCommand($this->globalService, $this->userService, $this->userSession, $this->userManager);
+ $listInput = new ArrayInput([], $listCommand->getDefinition());
+ $listInput->setOption('output', $input->getOption('output'));
+ $listInput->setOption('show-password', true);
+ $listCommand->listMounts($user, [$mount], $listInput, $output);
+ }
+
+ protected function getStorageService($userId) {
+ if (!empty($userId)) {
+ $user = $this->userManager->get($userId);
+ if (is_null($user)) {
+ throw new NoUserException("user $userId not found");
+ }
+ $this->userSession->setUser($user);
+ return $this->userService;
+ } else {
+ return $this->globalService;
+ }
+ }
+}
diff --git a/apps/files_external/lib/Command/Delete.php b/apps/files_external/lib/Command/Delete.php
new file mode 100644
index 00000000000..a3aef7f3303
--- /dev/null
+++ b/apps/files_external/lib/Command/Delete.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_external\NotFoundException;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCA\Files_External\Service\UserStoragesService;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+
+class Delete extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ protected $globalService;
+
+ /**
+ * @var UserStoragesService
+ */
+ protected $userService;
+
+ /**
+ * @var IUserSession
+ */
+ protected $userSession;
+
+ /**
+ * @var IUserManager
+ */
+ protected $userManager;
+
+ function __construct(GlobalStoragesService $globalService, UserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->userService = $userService;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:delete')
+ ->setDescription('Delete an external mount')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'The id of the mount to edit'
+ )->addOption(
+ 'yes',
+ 'y',
+ InputOption::VALUE_NONE,
+ 'Skip confirmation'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mountId = $input->getArgument('mount_id');
+ try {
+ $mount = $this->globalService->getStorage($mountId);
+ } catch (NotFoundException $e) {
+ $output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
+ return 404;
+ }
+
+ $noConfirm = $input->getOption('yes');
+
+ if (!$noConfirm) {
+ $listCommand = new ListCommand($this->globalService, $this->userService, $this->userSession, $this->userManager);
+ $listInput = new ArrayInput([], $listCommand->getDefinition());
+ $listInput->setOption('output', $input->getOption('output'));
+ $listCommand->listMounts(null, [$mount], $listInput, $output);
+
+ $questionHelper = $this->getHelper('question');
+ $question = new ConfirmationQuestion('Delete this mount? [y/N] ', false);
+
+ if (!$questionHelper->ask($input, $output, $question)) {
+ return;
+ }
+ }
+
+ $this->globalService->removeStorage($mountId);
+ }
+}
diff --git a/apps/files_external/lib/Command/Export.php b/apps/files_external/lib/Command/Export.php
new file mode 100644
index 00000000000..09c5ea8a9df
--- /dev/null
+++ b/apps/files_external/lib/Command/Export.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Helper\TableHelper;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\Input;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Export extends ListCommand {
+
+ protected function configure() {
+ $this
+ ->setName('files_external:export')
+ ->setDescription('Export mount configurations')
+ ->addArgument(
+ 'user_id',
+ InputArgument::OPTIONAL,
+ 'user id to export the personal mounts for, if no user is provided admin mounts will be exported'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $listCommand = new ListCommand($this->globalService, $this->userService, $this->userSession, $this->userManager);
+ $listInput = new ArrayInput([], $listCommand->getDefinition());
+ $listInput->setArgument('user_id', $input->getArgument('user_id'));
+ $listInput->setOption('output', 'json_pretty');
+ $listInput->setOption('show-password', true);
+ $listInput->setOption('full', true);
+ $listCommand->execute($listInput, $output);
+ }
+}
diff --git a/apps/files_external/lib/Command/Import.php b/apps/files_external/lib/Command/Import.php
new file mode 100644
index 00000000000..55528b66472
--- /dev/null
+++ b/apps/files_external/lib/Command/Import.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OC\User\NoUserException;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_External\Service\BackendService;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCA\Files_External\Service\ImportLegacyStoragesService;
+use OCA\Files_External\Service\UserStoragesService;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Import extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ private $globalService;
+
+ /**
+ * @var UserStoragesService
+ */
+ private $userService;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /** @var ImportLegacyStoragesService */
+ private $importLegacyStorageService;
+
+ /** @var BackendService */
+ private $backendService;
+
+ function __construct(GlobalStoragesService $globalService,
+ UserStoragesService $userService,
+ IUserSession $userSession,
+ IUserManager $userManager,
+ ImportLegacyStoragesService $importLegacyStorageService,
+ BackendService $backendService
+ ) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->userService = $userService;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+ $this->importLegacyStorageService = $importLegacyStorageService;
+ $this->backendService = $backendService;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:import')
+ ->setDescription('Import mount configurations')
+ ->addOption(
+ 'user',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'user to add the mount configurations for, if not set the mount will be added as system mount'
+ )
+ ->addArgument(
+ 'path',
+ InputArgument::REQUIRED,
+ 'path to a json file containing the mounts to import, use "-" to read from stdin'
+ )
+ ->addOption(
+ 'dry',
+ null,
+ InputOption::VALUE_NONE,
+ 'Don\'t save the imported mounts, only list the new mounts'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $user = $input->getOption('user');
+ $path = $input->getArgument('path');
+ if ($path === '-') {
+ $json = file_get_contents('php://stdin');
+ } else {
+ if (!file_exists($path)) {
+ $output->writeln('<error>File not found: ' . $path . '</error>');
+ return 1;
+ }
+ $json = file_get_contents($path);
+ }
+ if (!is_string($json) || strlen($json) < 2) {
+ $output->writeln('<error>Error while reading json</error>');
+ return 1;
+ }
+ $data = json_decode($json, true);
+ if (!is_array($data)) {
+ $output->writeln('<error>Error while parsing json</error>');
+ return 1;
+ }
+
+ $isLegacy = isset($data['user']) || isset($data['group']);
+ if ($isLegacy) {
+ $this->importLegacyStorageService->setData($data);
+ $mounts = $this->importLegacyStorageService->getAllStorages();
+ foreach ($mounts as $mount) {
+ if ($mount->getBackendOption('password') === false) {
+ $output->writeln('<error>Failed to decrypt password</error>');
+ return 1;
+ }
+ }
+ } else {
+ if (!isset($data[0])) { //normalize to an array of mounts
+ $data = [$data];
+ }
+ $mounts = array_map([$this, 'parseData'], $data);
+ }
+
+ if ($user) {
+ // ensure applicables are correct for personal mounts
+ foreach ($mounts as $mount) {
+ $mount->setApplicableGroups([]);
+ $mount->setApplicableUsers([$user]);
+ }
+ }
+
+ $storageService = $this->getStorageService($user);
+
+ $existingMounts = $storageService->getAllStorages();
+
+ foreach ($mounts as $mount) {
+ foreach ($existingMounts as $existingMount) {
+ if (
+ $existingMount->getMountPoint() === $mount->getMountPoint() &&
+ $existingMount->getApplicableGroups() === $mount->getApplicableGroups() &&
+ $existingMount->getApplicableUsers() == $mount->getApplicableUsers() &&
+ $existingMount->getBackendOptions() == $mount->getBackendOptions()
+ ) {
+ $output->writeln("<error>Duplicate mount (" . $mount->getMountPoint() . ")</error>");
+ return 1;
+ }
+ }
+ }
+
+ if ($input->getOption('dry')) {
+ if (count($mounts) === 0) {
+ $output->writeln('<error>No mounts to be imported</error>');
+ return 1;
+ }
+ $listCommand = new ListCommand($this->globalService, $this->userService, $this->userSession, $this->userManager);
+ $listInput = new ArrayInput([], $listCommand->getDefinition());
+ $listInput->setOption('output', $input->getOption('output'));
+ $listInput->setOption('show-password', true);
+ $listCommand->listMounts($user, $mounts, $listInput, $output);
+ } else {
+ foreach ($mounts as $mount) {
+ $storageService->addStorage($mount);
+ }
+ }
+ return 0;
+ }
+
+ private function parseData(array $data) {
+ $mount = new StorageConfig($data['mount_id']);
+ $mount->setMountPoint($data['mount_point']);
+ $mount->setBackend($this->getBackendByClass($data['storage']));
+ $authBackend = $this->backendService->getAuthMechanism($data['authentication_type']);
+ $mount->setAuthMechanism($authBackend);
+ $mount->setBackendOptions($data['configuration']);
+ $mount->setMountOptions($data['options']);
+ $mount->setApplicableUsers(isset($data['applicable_users']) ? $data['applicable_users'] : []);
+ $mount->setApplicableGroups(isset($data['applicable_groups']) ? $data['applicable_groups'] : []);
+ return $mount;
+ }
+
+ private function getBackendByClass($className) {
+ $backends = $this->backendService->getBackends();
+ foreach ($backends as $backend) {
+ if ($backend->getStorageClass() === $className) {
+ return $backend;
+ }
+ }
+ }
+
+ protected function getStorageService($userId) {
+ if (!empty($userId)) {
+ $user = $this->userManager->get($userId);
+ if (is_null($user)) {
+ throw new NoUserException("user $userId not found");
+ }
+ $this->userSession->setUser($user);
+ return $this->userService;
+ } else {
+ return $this->globalService;
+ }
+ }
+}
diff --git a/apps/files_external/lib/Command/ListCommand.php b/apps/files_external/lib/Command/ListCommand.php
new file mode 100644
index 00000000000..cbe1913ccfc
--- /dev/null
+++ b/apps/files_external/lib/Command/ListCommand.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OC\User\NoUserException;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCA\Files_External\Service\UserStoragesService;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListCommand extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ protected $globalService;
+
+ /**
+ * @var UserStoragesService
+ */
+ protected $userService;
+
+ /**
+ * @var IUserSession
+ */
+ protected $userSession;
+
+ /**
+ * @var IUserManager
+ */
+ protected $userManager;
+
+ function __construct(GlobalStoragesService $globalService, UserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->userService = $userService;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:list')
+ ->setDescription('List configured mounts')
+ ->addArgument(
+ 'user_id',
+ InputArgument::OPTIONAL,
+ 'user id to list the personal mounts for, if no user is provided admin mounts will be listed'
+ )->addOption(
+ 'show-password',
+ null,
+ InputOption::VALUE_NONE,
+ 'show passwords and secrets'
+ )->addOption(
+ 'full',
+ null,
+ InputOption::VALUE_NONE,
+ 'don\'t truncate long values in table output'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $userId = $input->getArgument('user_id');
+ $storageService = $this->getStorageService($userId);
+
+ /** @var $mounts StorageConfig[] */
+ $mounts = $storageService->getAllStorages();
+
+ $this->listMounts($userId, $mounts, $input, $output);
+ }
+
+ /**
+ * @param $userId $userId
+ * @param StorageConfig[] $mounts
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ */
+ public function listMounts($userId, array $mounts, InputInterface $input, OutputInterface $output){
+ $outputType = $input->getOption('output');
+ if (count($mounts) === 0) {
+ if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
+ $output->writeln('[]');
+ } else {
+ if ($userId) {
+ $output->writeln("<info>No mounts configured by $userId</info>");
+ } else {
+ $output->writeln("<info>No admin mounts configured</info>");
+ }
+ }
+ return;
+ }
+
+ $headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options'];
+
+ if (!$userId) {
+ $headers[] = 'Applicable Users';
+ $headers[] = 'Applicable Groups';
+ }
+
+ if (!$input->getOption('show-password')) {
+ $hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key'];
+ foreach ($mounts as $mount) {
+ $config = $mount->getBackendOptions();
+ foreach ($config as $key => $value) {
+ if (in_array($key, $hideKeys)) {
+ $mount->setBackendOption($key, '***');
+ }
+ }
+ }
+ }
+
+ if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
+ $keys = array_map(function ($header) {
+ return strtolower(str_replace(' ', '_', $header));
+ }, $headers);
+
+ $pairs = array_map(function (StorageConfig $config) use ($keys, $userId) {
+ $values = [
+ $config->getId(),
+ $config->getMountPoint(),
+ $config->getBackend()->getStorageClass(),
+ $config->getAuthMechanism()->getIdentifier(),
+ $config->getBackendOptions(),
+ $config->getMountOptions()
+ ];
+ if (!$userId) {
+ $values[] = $config->getApplicableUsers();
+ $values[] = $config->getApplicableGroups();
+ }
+
+ return array_combine($keys, $values);
+ }, $mounts);
+ if ($outputType === self::OUTPUT_FORMAT_JSON) {
+ $output->writeln(json_encode(array_values($pairs)));
+ } else {
+ $output->writeln(json_encode(array_values($pairs), JSON_PRETTY_PRINT));
+ }
+ } else {
+ $full = $input->getOption('full');
+ $defaultMountOptions = [
+ 'encrypt' => true,
+ 'previews' => true,
+ 'filesystem_check_changes' => 1
+ ];
+ $rows = array_map(function (StorageConfig $config) use ($userId, $defaultMountOptions, $full) {
+ $storageConfig = $config->getBackendOptions();
+ $keys = array_keys($storageConfig);
+ $values = array_values($storageConfig);
+
+ if (!$full) {
+ $values = array_map(function ($value) {
+ if (is_string($value) && strlen($value) > 32) {
+ return substr($value, 0, 6) . '...' . substr($value, -6, 6);
+ } else {
+ return $value;
+ }
+ }, $values);
+ }
+
+ $configStrings = array_map(function ($key, $value) {
+ return $key . ': ' . json_encode($value);
+ }, $keys, $values);
+ $configString = implode(', ', $configStrings);
+
+ $mountOptions = $config->getMountOptions();
+ // hide defaults
+ foreach ($mountOptions as $key => $value) {
+ if ($value === $defaultMountOptions[$key]) {
+ unset($mountOptions[$key]);
+ }
+ }
+ $keys = array_keys($mountOptions);
+ $values = array_values($mountOptions);
+
+ $optionsStrings = array_map(function ($key, $value) {
+ return $key . ': ' . json_encode($value);
+ }, $keys, $values);
+ $optionsString = implode(', ', $optionsStrings);
+
+ $values = [
+ $config->getId(),
+ $config->getMountPoint(),
+ $config->getBackend()->getText(),
+ $config->getAuthMechanism()->getText(),
+ $configString,
+ $optionsString
+ ];
+
+ if (!$userId) {
+ $applicableUsers = implode(', ', $config->getApplicableUsers());
+ $applicableGroups = implode(', ', $config->getApplicableGroups());
+ if ($applicableUsers === '' && $applicableGroups === '') {
+ $applicableUsers = 'All';
+ }
+ $values[] = $applicableUsers;
+ $values[] = $applicableGroups;
+ }
+
+ return $values;
+ }, $mounts);
+
+ $table = new Table($output);
+ $table->setHeaders($headers);
+ $table->setRows($rows);
+ $table->render();
+ }
+ }
+
+ protected function getStorageService($userId) {
+ if (!empty($userId)) {
+ $user = $this->userManager->get($userId);
+ if (is_null($user)) {
+ throw new NoUserException("user $userId not found");
+ }
+ $this->userSession->setUser($user);
+ return $this->userService;
+ } else {
+ return $this->globalService;
+ }
+ }
+}
diff --git a/apps/files_external/lib/Command/Option.php b/apps/files_external/lib/Command/Option.php
new file mode 100644
index 00000000000..109ca26c31f
--- /dev/null
+++ b/apps/files_external/lib/Command/Option.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OCA\Files_external\Lib\StorageConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Option extends Config {
+ protected function configure() {
+ $this
+ ->setName('files_external:option')
+ ->setDescription('Manage mount options for a mount')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'The id of the mount to edit'
+ )->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ 'key of the mount option to set/get'
+ )->addArgument(
+ 'value',
+ InputArgument::OPTIONAL,
+ 'value to set the mount option to, when no value is provided the existing value will be printed'
+ );
+ }
+
+ /**
+ * @param StorageConfig $mount
+ * @param string $key
+ * @param OutputInterface $output
+ */
+ protected function getOption(StorageConfig $mount, $key, OutputInterface $output) {
+ $value = $mount->getMountOption($key);
+ if (!is_string($value)) { // show bools and objects correctly
+ $value = json_encode($value);
+ }
+ $output->writeln($value);
+ }
+
+ /**
+ * @param StorageConfig $mount
+ * @param string $key
+ * @param string $value
+ * @param OutputInterface $output
+ */
+ protected function setOption(StorageConfig $mount, $key, $value, OutputInterface $output) {
+ $decoded = json_decode($value, true);
+ if (!is_null($decoded)) {
+ $value = $decoded;
+ }
+ $mount->setMountOption($key, $value);
+ $this->globalService->updateStorage($mount);
+ }
+}
diff --git a/apps/files_external/lib/Command/Verify.php b/apps/files_external/lib/Command/Verify.php
new file mode 100644
index 00000000000..6ff871a6094
--- /dev/null
+++ b/apps/files_external/lib/Command/Verify.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCA\Files_External\Lib\Backend\Backend;
+use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+use OCA\Files_external\Lib\StorageConfig;
+use OCA\Files_external\NotFoundException;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\Files\StorageNotAvailableException;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Verify extends Base {
+ /**
+ * @var GlobalStoragesService
+ */
+ protected $globalService;
+
+ function __construct(GlobalStoragesService $globalService) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:verify')
+ ->setDescription('Verify mount configuration')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'The id of the mount to check'
+ )->addOption(
+ 'config',
+ 'c',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Additional config option to set before checking in key=value pairs, required for certain auth backends such as login credentails'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mountId = $input->getArgument('mount_id');
+ $configInput = $input->getOption('config');
+
+ try {
+ $mount = $this->globalService->getStorage($mountId);
+ } catch (NotFoundException $e) {
+ $output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
+ return 404;
+ }
+
+ $this->updateStorageStatus($mount, $configInput, $output);
+
+ $this->writeArrayInOutputFormat($input, $output, [
+ 'status' => StorageNotAvailableException::getStateCodeName($mount->getStatus()),
+ 'code' => $mount->getStatus(),
+ 'message' => $mount->getStatusMessage()
+ ]);
+ }
+
+ private function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage);
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage);
+ }
+
+ private function updateStorageStatus(StorageConfig &$storage, $configInput, OutputInterface $output) {
+ try {
+ try {
+ $this->manipulateStorageConfig($storage);
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ if (count($configInput) === 0) { // extra config options might solve the error
+ throw $e;
+ }
+ }
+
+ foreach ($configInput as $configOption) {
+ if (!strpos($configOption, '=')) {
+ $output->writeln('<error>Invalid mount configuration option "' . $configOption . '"</error>');
+ return;
+ }
+ list($key, $value) = explode('=', $configOption, 2);
+ $storage->setBackendOption($key, $value);
+ }
+
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ // update status (can be time-consuming)
+ $storage->setStatus(
+ \OC_Mount_Config::getBackendStatus(
+ $backend->getStorageClass(),
+ $storage->getBackendOptions(),
+ false
+ )
+ );
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE;
+ $storage->setStatus(
+ $status,
+ $e->getMessage()
+ );
+ } catch (StorageNotAvailableException $e) {
+ $storage->setStatus(
+ $e->getCode(),
+ $e->getMessage()
+ );
+ } catch (\Exception $e) {
+ // FIXME: convert storage exceptions to StorageNotAvailableException
+ $storage->setStatus(
+ StorageNotAvailableException::STATUS_ERROR,
+ get_class($e) . ': ' . $e->getMessage()
+ );
+ }
+ }
+}
diff --git a/apps/files_external/lib/Controller/AjaxController.php b/apps/files_external/lib/Controller/AjaxController.php
new file mode 100644
index 00000000000..c3df3fa8522
--- /dev/null
+++ b/apps/files_external/lib/Controller/AjaxController.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Ross Nicoll <jrn@jrn.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Controller;
+
+use OCP\AppFramework\Controller;
+use OCP\IRequest;
+use OCP\AppFramework\Http\JSONResponse;
+use OCA\Files_External\Lib\Auth\PublicKey\RSA;
+
+class AjaxController extends Controller {
+ /** @var RSA */
+ private $rsaMechanism;
+
+ public function __construct($appName, IRequest $request, RSA $rsaMechanism) {
+ parent::__construct($appName, $request);
+ $this->rsaMechanism = $rsaMechanism;
+ }
+
+ private function generateSshKeys() {
+ $key = $this->rsaMechanism->createKey();
+ // Replace the placeholder label with a more meaningful one
+ $key['publicKey'] = str_replace('phpseclib-generated-key', gethostname(), $key['publickey']);
+
+ return $key;
+ }
+
+ /**
+ * Generates an SSH public/private key pair.
+ *
+ * @NoAdminRequired
+ */
+ public function getSshKeys() {
+ $key = $this->generateSshKeys();
+ return new JSONResponse(
+ array('data' => array(
+ 'private_key' => $key['privatekey'],
+ 'public_key' => $key['publickey']
+ ),
+ 'status' => 'success'
+ ));
+ }
+}
diff --git a/apps/files_external/lib/Controller/GlobalStoragesController.php b/apps/files_external/lib/Controller/GlobalStoragesController.php
new file mode 100644
index 00000000000..2471ac861de
--- /dev/null
+++ b/apps/files_external/lib/Controller/GlobalStoragesController.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Controller;
+
+
+use OCP\ILogger;
+use \OCP\IRequest;
+use \OCP\IL10N;
+use \OCP\AppFramework\Http\DataResponse;
+use \OCP\AppFramework\Http;
+use OCA\Files_External\Service\GlobalStoragesService;
+use \OCA\Files_external\NotFoundException;
+
+/**
+ * Global storages controller
+ */
+class GlobalStoragesController extends StoragesController {
+ /**
+ * Creates a new global storages controller.
+ *
+ * @param string $AppName application name
+ * @param IRequest $request request object
+ * @param IL10N $l10n l10n service
+ * @param GlobalStoragesService $globalStoragesService storage service
+ * @param ILogger $logger
+ */
+ public function __construct(
+ $AppName,
+ IRequest $request,
+ IL10N $l10n,
+ GlobalStoragesService $globalStoragesService,
+ ILogger $logger
+ ) {
+ parent::__construct(
+ $AppName,
+ $request,
+ $l10n,
+ $globalStoragesService,
+ $logger
+ );
+ }
+
+ /**
+ * Create an external storage entry.
+ *
+ * @param string $mountPoint storage mount point
+ * @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
+ * @param array $applicableGroups groups for which to mount the storage
+ * @param int $priority priority
+ *
+ * @return DataResponse
+ */
+ public function create(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions,
+ $applicableUsers,
+ $applicableGroups,
+ $priority
+ ) {
+ $newStorage = $this->createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions,
+ $applicableUsers,
+ $applicableGroups,
+ $priority
+ );
+ if ($newStorage instanceof DataResponse) {
+ return $newStorage;
+ }
+
+ $response = $this->validate($newStorage);
+ if (!empty($response)) {
+ return $response;
+ }
+
+ $newStorage = $this->service->addStorage($newStorage);
+
+ $this->updateStorageStatus($newStorage);
+
+ return new DataResponse(
+ $newStorage,
+ Http::STATUS_CREATED
+ );
+ }
+
+ /**
+ * Update an external storage entry.
+ *
+ * @param int $id storage id
+ * @param string $mountPoint storage mount point
+ * @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
+ * @param array $applicableGroups groups for which to mount the storage
+ * @param int $priority priority
+ *
+ * @return DataResponse
+ */
+ public function update(
+ $id,
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions,
+ $applicableUsers,
+ $applicableGroups,
+ $priority
+ ) {
+ $storage = $this->createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions,
+ $applicableUsers,
+ $applicableGroups,
+ $priority
+ );
+ if ($storage instanceof DataResponse) {
+ return $storage;
+ }
+ $storage->setId($id);
+
+ $response = $this->validate($storage);
+ if (!empty($response)) {
+ return $response;
+ }
+
+ try {
+ $storage = $this->service->updateStorage($storage);
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ $this->updateStorageStatus($storage);
+
+ return new DataResponse(
+ $storage,
+ Http::STATUS_OK
+ );
+
+ }
+
+
+}
diff --git a/apps/files_external/lib/Controller/StoragesController.php b/apps/files_external/lib/Controller/StoragesController.php
new file mode 100644
index 00000000000..da26f24b6b8
--- /dev/null
+++ b/apps/files_external/lib/Controller/StoragesController.php
@@ -0,0 +1,338 @@
+<?php
+/**
+ * @author Jesús Macias <jmacias@solidgear.es>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Controller;
+
+
+use OCP\ILogger;
+use \OCP\IRequest;
+use \OCP\IL10N;
+use \OCP\AppFramework\Http\DataResponse;
+use \OCP\AppFramework\Controller;
+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;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
+use \OCP\Files\StorageNotAvailableException;
+use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+
+/**
+ * Base class for storages controllers
+ */
+abstract class StoragesController extends Controller {
+
+ /**
+ * L10N service
+ *
+ * @var IL10N
+ */
+ protected $l10n;
+
+ /**
+ * Storages service
+ *
+ * @var StoragesService
+ */
+ protected $service;
+
+ /**
+ * @var ILogger
+ */
+ protected $logger;
+
+ /**
+ * Creates a new storages controller.
+ *
+ * @param string $AppName application name
+ * @param IRequest $request request object
+ * @param IL10N $l10n l10n service
+ * @param StoragesService $storagesService storage service
+ * @param ILogger $logger
+ */
+ public function __construct(
+ $AppName,
+ IRequest $request,
+ IL10N $l10n,
+ StoragesService $storagesService,
+ ILogger $logger
+ ) {
+ parent::__construct($AppName, $request);
+ $this->l10n = $l10n;
+ $this->service = $storagesService;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Create a storage from its parameters
+ *
+ * @param string $mountPoint storage mount point
+ * @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
+ * @param array|null $applicableGroups groups for which to mount the storage
+ * @param int|null $priority priority
+ *
+ * @return StorageConfig|DataResponse
+ */
+ protected function createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions = null,
+ $applicableUsers = null,
+ $applicableGroups = null,
+ $priority = null
+ ) {
+ try {
+ return $this->service->createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions,
+ $applicableUsers,
+ $applicableGroups,
+ $priority
+ );
+ } catch (\InvalidArgumentException $e) {
+ $this->logger->logException($e);
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class')
+ ],
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+ }
+
+ /**
+ * Validate storage config
+ *
+ * @param StorageConfig $storage storage config
+ *1
+ * @return DataResponse|null returns response in case of validation error
+ */
+ protected function validate(StorageConfig $storage) {
+ $mountPoint = $storage->getMountPoint();
+ if ($mountPoint === '' || $mountPoint === '/') {
+ return new DataResponse(
+ array(
+ 'message' => (string)$this->l10n->t('Invalid mount point')
+ ),
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+
+ if ($storage->getBackendOption('objectstore')) {
+ // objectstore must not be sent from client side
+ return new DataResponse(
+ array(
+ 'message' => (string)$this->l10n->t('Objectstore forbidden')
+ ),
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ if ($backend->checkDependencies()) {
+ // invalid backend
+ return new DataResponse(
+ array(
+ 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [
+ $backend->getIdentifier()
+ ])
+ ),
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+
+ if (!$backend->isVisibleFor($this->service->getVisibilityType())) {
+ // not permitted to use backend
+ return new DataResponse(
+ array(
+ 'message' => (string)$this->l10n->t('Not permitted to use backend "%s"', [
+ $backend->getIdentifier()
+ ])
+ ),
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+ if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) {
+ // not permitted to use auth mechanism
+ return new DataResponse(
+ array(
+ 'message' => (string)$this->l10n->t('Not permitted to use authentication mechanism "%s"', [
+ $authMechanism->getIdentifier()
+ ])
+ ),
+ 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
+ );
+ }
+ if (!$authMechanism->validateStorage($storage)) {
+ // unsatisfied parameters
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Unsatisfied authentication mechanism parameters')
+ ],
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
+
+ return null;
+ }
+
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage);
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage);
+ }
+
+ /**
+ * Check whether the given storage is available / valid.
+ *
+ * Note that this operation can be time consuming depending
+ * on whether the remote storage is available or not.
+ *
+ * @param StorageConfig $storage storage configuration
+ */
+ protected function updateStorageStatus(StorageConfig &$storage) {
+ try {
+ $this->manipulateStorageConfig($storage);
+
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ // update status (can be time-consuming)
+ $storage->setStatus(
+ \OC_Mount_Config::getBackendStatus(
+ $backend->getStorageClass(),
+ $storage->getBackendOptions(),
+ false
+ )
+ );
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE;
+ $storage->setStatus(
+ $status,
+ $this->l10n->t('Insufficient data: %s', [$e->getMessage()])
+ );
+ } catch (StorageNotAvailableException $e) {
+ $storage->setStatus(
+ $e->getCode(),
+ $this->l10n->t('%s', [$e->getMessage()])
+ );
+ } catch (\Exception $e) {
+ // FIXME: convert storage exceptions to StorageNotAvailableException
+ $storage->setStatus(
+ StorageNotAvailableException::STATUS_ERROR,
+ get_class($e).': '.$e->getMessage()
+ );
+ }
+ }
+
+ /**
+ * Get all storage entries
+ *
+ * @return DataResponse
+ */
+ public function index() {
+ $storages = $this->service->getStorages();
+
+ return new DataResponse(
+ $storages,
+ Http::STATUS_OK
+ );
+ }
+
+ /**
+ * Get an external storage entry.
+ *
+ * @param int $id storage id
+ *
+ * @return DataResponse
+ */
+ public function show($id) {
+ try {
+ $storage = $this->service->getStorage($id);
+
+ $this->updateStorageStatus($storage);
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ return new DataResponse(
+ $storage,
+ Http::STATUS_OK
+ );
+ }
+
+ /**
+ * Deletes the storage with the given id.
+ *
+ * @param int $id storage id
+ *
+ * @return DataResponse
+ */
+ public function destroy($id) {
+ try {
+ $this->service->removeStorage($id);
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ return new DataResponse([], Http::STATUS_NO_CONTENT);
+ }
+
+}
+
diff --git a/apps/files_external/lib/Controller/UserGlobalStoragesController.php b/apps/files_external/lib/Controller/UserGlobalStoragesController.php
new file mode 100644
index 00000000000..c420d9ad59f
--- /dev/null
+++ b/apps/files_external/lib/Controller/UserGlobalStoragesController.php
@@ -0,0 +1,201 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Controller;
+
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCA\Files_External\Lib\Auth\IUserProvided;
+use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+use OCP\ILogger;
+use \OCP\IRequest;
+use \OCP\IL10N;
+use \OCP\AppFramework\Http\DataResponse;
+use \OCP\AppFramework\Http;
+use OCA\Files_External\Service\UserGlobalStoragesService;
+use \OCA\Files_external\NotFoundException;
+use \OCA\Files_external\Lib\StorageConfig;
+use \OCA\Files_External\Lib\Backend\Backend;
+use OCP\IUserSession;
+
+/**
+ * User global storages controller
+ */
+class UserGlobalStoragesController extends StoragesController {
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * Creates a new user global storages controller.
+ *
+ * @param string $AppName application name
+ * @param IRequest $request request object
+ * @param IL10N $l10n l10n service
+ * @param UserGlobalStoragesService $userGlobalStoragesService storage service
+ * @param IUserSession $userSession
+ */
+ public function __construct(
+ $AppName,
+ IRequest $request,
+ IL10N $l10n,
+ UserGlobalStoragesService $userGlobalStoragesService,
+ IUserSession $userSession,
+ ILogger $logger
+ ) {
+ parent::__construct(
+ $AppName,
+ $request,
+ $l10n,
+ $userGlobalStoragesService,
+ $logger
+ );
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * Get all storage entries
+ *
+ * @return DataResponse
+ *
+ * @NoAdminRequired
+ */
+ public function index() {
+ $storages = $this->service->getUniqueStorages();
+
+ // remove configuration data, this must be kept private
+ foreach ($storages as $storage) {
+ $this->sanitizeStorage($storage);
+ }
+
+ return new DataResponse(
+ $storages,
+ Http::STATUS_OK
+ );
+ }
+
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
+ }
+
+ /**
+ * Get an external storage entry.
+ *
+ * @param int $id storage id
+ * @return DataResponse
+ *
+ * @NoAdminRequired
+ */
+ public function show($id) {
+ try {
+ $storage = $this->service->getStorage($id);
+
+ $this->updateStorageStatus($storage);
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ $this->sanitizeStorage($storage);
+
+ return new DataResponse(
+ $storage,
+ Http::STATUS_OK
+ );
+ }
+
+ /**
+ * Update an external storage entry.
+ * Only allows setting user provided backend fields
+ *
+ * @param int $id storage id
+ * @param array $backendOptions backend-specific options
+ *
+ * @return DataResponse
+ *
+ * @NoAdminRequired
+ */
+ public function update(
+ $id,
+ $backendOptions
+ ) {
+ try {
+ $storage = $this->service->getStorage($id);
+ $authMechanism = $storage->getAuthMechanism();
+ if ($authMechanism instanceof IUserProvided) {
+ $authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions);
+ $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
+ } else {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" is not user editable', array($id))
+ ],
+ Http::STATUS_FORBIDDEN
+ );
+ }
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ $this->updateStorageStatus($storage);
+ $this->sanitizeStorage($storage);
+
+ return new DataResponse(
+ $storage,
+ Http::STATUS_OK
+ );
+
+ }
+
+ /**
+ * Remove sensitive data from a StorageConfig before returning it to the user
+ *
+ * @param StorageConfig $storage
+ */
+ protected function sanitizeStorage(StorageConfig $storage) {
+ $storage->setBackendOptions([]);
+ $storage->setMountOptions([]);
+
+ if ($storage->getAuthMechanism() instanceof IUserProvided) {
+ try {
+ $storage->getAuthMechanism()->manipulateStorageConfig($storage, $this->userSession->getUser());
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ // not configured yet
+ }
+ }
+ }
+
+}
diff --git a/apps/files_external/lib/Controller/UserStoragesController.php b/apps/files_external/lib/Controller/UserStoragesController.php
new file mode 100644
index 00000000000..e20a41e0cc4
--- /dev/null
+++ b/apps/files_external/lib/Controller/UserStoragesController.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Controller;
+
+
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCP\ILogger;
+use \OCP\IUserSession;
+use \OCP\IRequest;
+use \OCP\IL10N;
+use \OCP\AppFramework\Http\DataResponse;
+use \OCP\AppFramework\Http;
+use OCA\Files_External\Service\UserStoragesService;
+use \OCA\Files_external\NotFoundException;
+use \OCA\Files_external\Lib\StorageConfig;
+use \OCA\Files_External\Lib\Backend\Backend;
+
+/**
+ * User storages controller
+ */
+class UserStoragesController extends StoragesController {
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * Creates a new user storages controller.
+ *
+ * @param string $AppName application name
+ * @param IRequest $request request object
+ * @param IL10N $l10n l10n service
+ * @param UserStoragesService $userStoragesService storage service
+ * @param IUserSession $userSession
+ * @param ILogger $logger
+ */
+ public function __construct(
+ $AppName,
+ IRequest $request,
+ IL10N $l10n,
+ UserStoragesService $userStoragesService,
+ IUserSession $userSession,
+ ILogger $logger
+ ) {
+ parent::__construct(
+ $AppName,
+ $request,
+ $l10n,
+ $userStoragesService,
+ $logger
+ );
+ $this->userSession = $userSession;
+ }
+
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
+ }
+
+ /**
+ * Get all storage entries
+ *
+ * @NoAdminRequired
+ *
+ * @return DataResponse
+ */
+ public function index() {
+ return parent::index();
+ }
+
+ /**
+ * Return storage
+ *
+ * @NoAdminRequired
+ *
+ * {@inheritdoc}
+ */
+ public function show($id) {
+ return parent::show($id);
+ }
+
+ /**
+ * Create an external storage entry.
+ *
+ * @param string $mountPoint storage mount point
+ * @param string $backend backend identifier
+ * @param string $authMechanism authentication mechanism identifier
+ * @param array $backendOptions backend-specific options
+ * @param array $mountOptions backend-specific mount options
+ *
+ * @return DataResponse
+ *
+ * @NoAdminRequired
+ */
+ public function create(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions
+ ) {
+ $newStorage = $this->createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions
+ );
+ if ($newStorage instanceOf DataResponse) {
+ return $newStorage;
+ }
+
+ $response = $this->validate($newStorage);
+ if (!empty($response)) {
+ return $response;
+ }
+
+ $newStorage = $this->service->addStorage($newStorage);
+ $this->updateStorageStatus($newStorage);
+
+ return new DataResponse(
+ $newStorage,
+ Http::STATUS_CREATED
+ );
+ }
+
+ /**
+ * Update an external storage entry.
+ *
+ * @param int $id storage id
+ * @param string $mountPoint storage mount point
+ * @param string $backend backend identifier
+ * @param string $authMechanism authentication mechanism identifier
+ * @param array $backendOptions backend-specific options
+ * @param array $mountOptions backend-specific mount options
+ *
+ * @return DataResponse
+ *
+ * @NoAdminRequired
+ */
+ public function update(
+ $id,
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions
+ ) {
+ $storage = $this->createStorage(
+ $mountPoint,
+ $backend,
+ $authMechanism,
+ $backendOptions,
+ $mountOptions
+ );
+ if ($storage instanceOf DataResponse) {
+ return $storage;
+ }
+ $storage->setId($id);
+
+ $response = $this->validate($storage);
+ if (!empty($response)) {
+ return $response;
+ }
+
+ try {
+ $storage = $this->service->updateStorage($storage);
+ } catch (NotFoundException $e) {
+ return new DataResponse(
+ [
+ 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id))
+ ],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ $this->updateStorageStatus($storage);
+
+ return new DataResponse(
+ $storage,
+ Http::STATUS_OK
+ );
+
+ }
+
+ /**
+ * Delete storage
+ *
+ * @NoAdminRequired
+ *
+ * {@inheritdoc}
+ */
+ public function destroy($id) {
+ return parent::destroy($id);
+ }
+
+}
diff --git a/apps/files_external/lib/Migration/DummyUserSession.php b/apps/files_external/lib/Migration/DummyUserSession.php
new file mode 100644
index 00000000000..b2157aafebb
--- /dev/null
+++ b/apps/files_external/lib/Migration/DummyUserSession.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Migration;
+
+use OCP\IUser;
+use OCP\IUserSession;
+
+class DummyUserSession implements IUserSession {
+
+ /**
+ * @var IUser
+ */
+ private $user;
+
+ public function login($user, $password) {
+ }
+
+ public function logout() {
+ }
+
+ public function setUser($user) {
+ $this->user = $user;
+ }
+
+ public function getUser() {
+ return $this->user;
+ }
+
+ public function isLoggedIn() {
+ return !is_null($this->user);
+ }
+}
diff --git a/apps/files_external/lib/Migration/StorageMigrator.php b/apps/files_external/lib/Migration/StorageMigrator.php
new file mode 100644
index 00000000000..a8b0461e7de
--- /dev/null
+++ b/apps/files_external/lib/Migration/StorageMigrator.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Migration;
+
+use OCA\Files_External\Service\BackendService;
+use OCA\Files_External\Service\DBConfigService;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCA\Files_External\Service\LegacyStoragesService;
+use OCA\Files_External\Service\StoragesService;
+use OCP\Files\Config\IUserMountCache;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\ILogger;
+use OCP\IUser;
+
+/**
+ * Migrate mount config from mount.json to the database
+ */
+class StorageMigrator {
+ /**
+ * @var BackendService
+ */
+ private $backendService;
+
+ /**
+ * @var DBConfigService
+ */
+ private $dbConfig;
+
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ /**
+ * @var ILogger
+ */
+ private $logger;
+
+ /** @var IUserMountCache */
+ private $userMountCache;
+
+ /**
+ * StorageMigrator constructor.
+ *
+ * @param BackendService $backendService
+ * @param DBConfigService $dbConfig
+ * @param IConfig $config
+ * @param IDBConnection $connection
+ * @param ILogger $logger
+ * @param IUserMountCache $userMountCache
+ */
+ public function __construct(
+ BackendService $backendService,
+ DBConfigService $dbConfig,
+ IConfig $config,
+ IDBConnection $connection,
+ ILogger $logger,
+ IUserMountCache $userMountCache
+ ) {
+ $this->backendService = $backendService;
+ $this->dbConfig = $dbConfig;
+ $this->config = $config;
+ $this->connection = $connection;
+ $this->logger = $logger;
+ $this->userMountCache = $userMountCache;
+ }
+
+ private function migrate(LegacyStoragesService $legacyService, StoragesService $storageService) {
+ $existingStorage = $legacyService->getAllStorages();
+
+ $this->connection->beginTransaction();
+ try {
+ foreach ($existingStorage as $storage) {
+ $mountOptions = $storage->getMountOptions();
+ if (!empty($mountOptions) && !isset($mountOptions['enable_sharing'])) {
+ // existing mounts must have sharing enabled by default to avoid surprises
+ $mountOptions['enable_sharing'] = true;
+ $storage->setMountOptions($mountOptions);
+ }
+ $storageService->addStorage($storage);
+ }
+ $this->connection->commit();
+ } catch (\Exception $e) {
+ $this->logger->logException($e);
+ $this->connection->rollBack();
+ }
+ }
+
+ /**
+ * Migrate admin configured storages
+ */
+ public function migrateGlobal() {
+ $legacyService = new GlobalLegacyStoragesService($this->backendService);
+ $storageService = new GlobalStoragesService($this->backendService, $this->dbConfig, $this->userMountCache);
+
+ $this->migrate($legacyService, $storageService);
+ }
+
+ /**
+ * Migrate personal storages configured by the current user
+ *
+ * @param IUser $user
+ */
+ public function migrateUser(IUser $user) {
+ $dummySession = new DummyUserSession();
+ $dummySession->setUser($user);
+ $userId = $user->getUID();
+ $userVersion = $this->config->getUserValue($userId, 'files_external', 'config_version', '0.0.0');
+ if (version_compare($userVersion, '0.5.0', '<')) {
+ $this->config->setUserValue($userId, 'files_external', 'config_version', '0.5.0');
+ $legacyService = new UserLegacyStoragesService($this->backendService, $dummySession);
+ $storageService = new UserStoragesService($this->backendService, $this->dbConfig, $dummySession, $this->userMountCache);
+
+ $this->migrate($legacyService, $storageService);
+ }
+ }
+}
diff --git a/apps/files_external/lib/Service/BackendService.php b/apps/files_external/lib/Service/BackendService.php
new file mode 100644
index 00000000000..c3dc4da0177
--- /dev/null
+++ b/apps/files_external/lib/Service/BackendService.php
@@ -0,0 +1,282 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use \OCP\IConfig;
+
+use \OCA\Files_External\Lib\Backend\Backend;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
+use \OCA\Files_External\Lib\Config\IBackendProvider;
+use \OCA\Files_External\Lib\Config\IAuthMechanismProvider;
+
+/**
+ * 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 = [];
+
+ /** @var IBackendProvider[] */
+ private $backendProviders = [];
+
+ /** @var AuthMechanism[] */
+ private $authMechanisms = [];
+
+ /** @var IAuthMechanismProvider[] */
+ private $authMechanismProviders = [];
+
+ /**
+ * @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', '')
+ );
+
+ // if no backend is in the list an empty string is in the array and user mounting is disabled
+ if ($this->userMountingBackends === ['']) {
+ $this->userMountingAllowed = false;
+ }
+ }
+
+ /**
+ * Register a backend provider
+ *
+ * @since 9.1.0
+ * @param IBackendProvider $provider
+ */
+ public function registerBackendProvider(IBackendProvider $provider) {
+ $this->backendProviders[] = $provider;
+ }
+
+ private function loadBackendProviders() {
+ foreach ($this->backendProviders as $provider) {
+ $this->registerBackends($provider->getBackends());
+ }
+ $this->backendProviders = [];
+ }
+
+ /**
+ * Register an auth mechanism provider
+ *
+ * @since 9.1.0
+ * @param IAuthMechanismProvider $provider
+ */
+ public function registerAuthMechanismProvider(IAuthMechanismProvider $provider) {
+ $this->authMechanismProviders[] = $provider;
+ }
+
+ private function loadAuthMechanismProviders() {
+ foreach ($this->authMechanismProviders as $provider) {
+ $this->registerAuthMechanisms($provider->getAuthMechanisms());
+ }
+ $this->authMechanismProviders = [];
+ }
+
+ /**
+ * Register a backend
+ *
+ * @deprecated 9.1.0 use registerBackendProvider()
+ * @param Backend $backend
+ */
+ public function registerBackend(Backend $backend) {
+ if (!$this->isAllowedUserBackend($backend)) {
+ $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL);
+ }
+ foreach ($backend->getIdentifierAliases() as $alias) {
+ $this->backends[$alias] = $backend;
+ }
+ }
+
+ /**
+ * @deprecated 9.1.0 use registerBackendProvider()
+ * @param Backend[] $backends
+ */
+ public function registerBackends(array $backends) {
+ foreach ($backends as $backend) {
+ $this->registerBackend($backend);
+ }
+ }
+ /**
+ * Register an authentication mechanism
+ *
+ * @deprecated 9.1.0 use registerAuthMechanismProvider()
+ * @param AuthMechanism $authMech
+ */
+ public function registerAuthMechanism(AuthMechanism $authMech) {
+ if (!$this->isAllowedAuthMechanism($authMech)) {
+ $authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL);
+ }
+ foreach ($authMech->getIdentifierAliases() as $alias) {
+ $this->authMechanisms[$alias] = $authMech;
+ }
+ }
+
+ /**
+ * @deprecated 9.1.0 use registerAuthMechanismProvider()
+ * @param AuthMechanism[] $mechanisms
+ */
+ public function registerAuthMechanisms(array $mechanisms) {
+ foreach ($mechanisms as $mechanism) {
+ $this->registerAuthMechanism($mechanism);
+ }
+ }
+
+ /**
+ * Get all backends
+ *
+ * @return Backend[]
+ */
+ public function getBackends() {
+ $this->loadBackendProviders();
+ // only return real identifiers, no aliases
+ $backends = [];
+ foreach ($this->backends as $backend) {
+ $backends[$backend->getIdentifier()] = $backend;
+ }
+ return $backends;
+ }
+
+ /**
+ * Get all available backends
+ *
+ * @return Backend[]
+ */
+ public function getAvailableBackends() {
+ return array_filter($this->getBackends(), function($backend) {
+ return !($backend->checkDependencies());
+ });
+ }
+
+ /**
+ * @param string $identifier
+ * @return Backend|null
+ */
+ public function getBackend($identifier) {
+ $this->loadBackendProviders();
+ if (isset($this->backends[$identifier])) {
+ return $this->backends[$identifier];
+ }
+ return null;
+ }
+
+ /**
+ * Get all authentication mechanisms
+ *
+ * @return AuthMechanism[]
+ */
+ public function getAuthMechanisms() {
+ $this->loadAuthMechanismProviders();
+ // only return real identifiers, no aliases
+ $mechanisms = [];
+ foreach ($this->authMechanisms as $mechanism) {
+ $mechanisms[$mechanism->getIdentifier()] = $mechanism;
+ }
+ return $mechanisms;
+ }
+
+ /**
+ * 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);
+ });
+ }
+
+ /**
+ * @param string $identifier
+ * @return AuthMechanism|null
+ */
+ public function getAuthMechanism($identifier) {
+ $this->loadAuthMechanismProviders();
+ if (isset($this->authMechanisms[$identifier])) {
+ return $this->authMechanisms[$identifier];
+ }
+ 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 &&
+ array_intersect($backend->getIdentifierAliases(), $this->userMountingBackends)
+ ) {
+ return true;
+ }
+ 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/lib/Service/DBConfigService.php b/apps/files_external/lib/Service/DBConfigService.php
new file mode 100644
index 00000000000..9f7061eb938
--- /dev/null
+++ b/apps/files_external/lib/Service/DBConfigService.php
@@ -0,0 +1,451 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Security\ICrypto;
+
+/**
+ * Stores the mount config in the database
+ */
+class DBConfigService {
+ const MOUNT_TYPE_ADMIN = 1;
+ const MOUNT_TYPE_PERSONAl = 2;
+
+ const APPLICABLE_TYPE_GLOBAL = 1;
+ const APPLICABLE_TYPE_GROUP = 2;
+ const APPLICABLE_TYPE_USER = 3;
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ /**
+ * @var ICrypto
+ */
+ private $crypto;
+
+ /**
+ * DBConfigService constructor.
+ *
+ * @param IDBConnection $connection
+ * @param ICrypto $crypto
+ */
+ public function __construct(IDBConnection $connection, ICrypto $crypto) {
+ $this->connection = $connection;
+ $this->crypto = $crypto;
+ }
+
+ /**
+ * @param int $mountId
+ * @return array
+ */
+ public function getMountById($mountId) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
+ ->from('external_mounts', 'm')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+ $mounts = $this->getMountsFromQuery($query);
+ if (count($mounts) > 0) {
+ return $mounts[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get admin defined mounts
+ *
+ * @return array
+ */
+ public function getAdminMounts() {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
+ ->from('external_mounts')
+ ->where($builder->expr()->eq('type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
+ return $this->getMountsFromQuery($query);
+ }
+
+ protected function getForQuery(IQueryBuilder $builder, $type, $value) {
+ $query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
+ ->from('external_mounts', 'm')
+ ->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
+ ->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
+
+ if (is_null($value)) {
+ $query = $query->andWhere($builder->expr()->isNull('a.value'));
+ } else {
+ $query = $query->andWhere($builder->expr()->eq('a.value', $builder->createNamedParameter($value)));
+ }
+
+ return $query;
+ }
+
+ /**
+ * Get mounts by applicable
+ *
+ * @param int $type any of the self::APPLICABLE_TYPE_ constants
+ * @param string|null $value user_id, group_id or null for global mounts
+ * @return array
+ */
+ public function getMountsFor($type, $value) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $this->getForQuery($builder, $type, $value);
+
+ return $this->getMountsFromQuery($query);
+ }
+
+ /**
+ * Get admin defined mounts by applicable
+ *
+ * @param int $type any of the self::APPLICABLE_TYPE_ constants
+ * @param string|null $value user_id, group_id or null for global mounts
+ * @return array
+ */
+ public function getAdminMountsFor($type, $value) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $this->getForQuery($builder, $type, $value);
+ $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
+
+ return $this->getMountsFromQuery($query);
+ }
+
+ /**
+ * Get admin defined mounts for multiple applicable
+ *
+ * @param int $type any of the self::APPLICABLE_TYPE_ constants
+ * @param string[] $values user_ids or group_ids
+ * @return array
+ */
+ public function getAdminMountsForMultiple($type, array $values) {
+ $builder = $this->connection->getQueryBuilder();
+ $params = array_map(function ($value) use ($builder) {
+ return $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR);
+ }, $values);
+
+ $query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
+ ->from('external_mounts', 'm')
+ ->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
+ ->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
+ ->andWhere($builder->expr()->in('a.value', $params));
+ $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
+
+ return $this->getMountsFromQuery($query);
+ }
+
+ /**
+ * Get user defined mounts by applicable
+ *
+ * @param int $type any of the self::APPLICABLE_TYPE_ constants
+ * @param string|null $value user_id, group_id or null for global mounts
+ * @return array
+ */
+ public function getUserMountsFor($type, $value) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $this->getForQuery($builder, $type, $value);
+ $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_PERSONAl, IQueryBuilder::PARAM_INT)));
+
+ return $this->getMountsFromQuery($query);
+ }
+
+ /**
+ * Add a mount to the database
+ *
+ * @param string $mountPoint
+ * @param string $storageBackend
+ * @param string $authBackend
+ * @param int $priority
+ * @param int $type self::MOUNT_TYPE_ADMIN or self::MOUNT_TYPE_PERSONAL
+ * @return int the id of the new mount
+ */
+ public function addMount($mountPoint, $storageBackend, $authBackend, $priority, $type) {
+ if (!$priority) {
+ $priority = 100;
+ }
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->insert('external_mounts')
+ ->values([
+ 'mount_point' => $builder->createNamedParameter($mountPoint, IQueryBuilder::PARAM_STR),
+ 'storage_backend' => $builder->createNamedParameter($storageBackend, IQueryBuilder::PARAM_STR),
+ 'auth_backend' => $builder->createNamedParameter($authBackend, IQueryBuilder::PARAM_STR),
+ 'priority' => $builder->createNamedParameter($priority, IQueryBuilder::PARAM_INT),
+ 'type' => $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)
+ ]);
+ $query->execute();
+ return (int)$this->connection->lastInsertId('external_mounts');
+ }
+
+ /**
+ * Remove a mount from the database
+ *
+ * @param int $mountId
+ */
+ public function removeMount($mountId) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->delete('external_mounts')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+ $query->execute();
+
+ $query = $builder->delete('external_applicable')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+ $query->execute();
+
+ $query = $builder->delete('external_config')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+ $query->execute();
+
+ $query = $builder->delete('external_options')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+ $query->execute();
+ }
+
+ /**
+ * @param int $mountId
+ * @param string $newMountPoint
+ */
+ public function setMountPoint($mountId, $newMountPoint) {
+ $builder = $this->connection->getQueryBuilder();
+
+ $query = $builder->update('external_mounts')
+ ->set('mount_point', $builder->createNamedParameter($newMountPoint))
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+
+ $query->execute();
+ }
+
+ /**
+ * @param int $mountId
+ * @param string $newAuthBackend
+ */
+ public function setAuthBackend($mountId, $newAuthBackend) {
+ $builder = $this->connection->getQueryBuilder();
+
+ $query = $builder->update('external_mounts')
+ ->set('auth_backend', $builder->createNamedParameter($newAuthBackend))
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
+
+ $query->execute();
+ }
+
+ /**
+ * @param int $mountId
+ * @param string $key
+ * @param string $value
+ */
+ public function setConfig($mountId, $key, $value) {
+ if ($key === 'password') {
+ $value = $this->encryptValue($value);
+ }
+ $count = $this->connection->insertIfNotExist('*PREFIX*external_config', [
+ 'mount_id' => $mountId,
+ 'key' => $key,
+ 'value' => $value
+ ], ['mount_id', 'key']);
+ if ($count === 0) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->update('external_config')
+ ->set('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR))
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
+ ->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
+ $query->execute();
+ }
+ }
+
+ /**
+ * @param int $mountId
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption($mountId, $key, $value) {
+
+ $count = $this->connection->insertIfNotExist('*PREFIX*external_options', [
+ 'mount_id' => $mountId,
+ 'key' => $key,
+ 'value' => json_encode($value)
+ ], ['mount_id', 'key']);
+ if ($count === 0) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->update('external_options')
+ ->set('value', $builder->createNamedParameter(json_encode($value), IQueryBuilder::PARAM_STR))
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
+ ->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
+ $query->execute();
+ }
+ }
+
+ public function addApplicable($mountId, $type, $value) {
+ $this->connection->insertIfNotExist('*PREFIX*external_applicable', [
+ 'mount_id' => $mountId,
+ 'type' => $type,
+ 'value' => $value
+ ], ['mount_id', 'type', 'value']);
+ }
+
+ public function removeApplicable($mountId, $type, $value) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->delete('external_applicable')
+ ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
+ ->andWhere($builder->expr()->eq('type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
+
+ if (is_null($value)) {
+ $query = $query->andWhere($builder->expr()->isNull('value'));
+ } else {
+ $query = $query->andWhere($builder->expr()->eq('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR)));
+ }
+
+ $query->execute();
+ }
+
+ private function getMountsFromQuery(IQueryBuilder $query) {
+ $result = $query->execute();
+ $mounts = $result->fetchAll();
+ $uniqueMounts = [];
+ foreach ($mounts as $mount) {
+ $id = $mount['mount_id'];
+ if (!isset($uniqueMounts[$id])) {
+ $uniqueMounts[$id] = $mount;
+ }
+ }
+ $uniqueMounts = array_values($uniqueMounts);
+
+ $mountIds = array_map(function ($mount) {
+ return $mount['mount_id'];
+ }, $uniqueMounts);
+ $mountIds = array_values(array_unique($mountIds));
+
+ $applicable = $this->getApplicableForMounts($mountIds);
+ $config = $this->getConfigForMounts($mountIds);
+ $options = $this->getOptionsForMounts($mountIds);
+
+ return array_map(function ($mount, $applicable, $config, $options) {
+ $mount['type'] = (int)$mount['type'];
+ $mount['priority'] = (int)$mount['priority'];
+ $mount['applicable'] = $applicable;
+ $mount['config'] = $config;
+ $mount['options'] = $options;
+ return $mount;
+ }, $uniqueMounts, $applicable, $config, $options);
+ }
+
+ /**
+ * Get mount options from a table grouped by mount id
+ *
+ * @param string $table
+ * @param string[] $fields
+ * @param int[] $mountIds
+ * @return array [$mountId => [['field1' => $value1, ...], ...], ...]
+ */
+ private function selectForMounts($table, array $fields, array $mountIds) {
+ if (count($mountIds) === 0) {
+ return [];
+ }
+ $builder = $this->connection->getQueryBuilder();
+ $fields[] = 'mount_id';
+ $placeHolders = array_map(function ($id) use ($builder) {
+ return $builder->createPositionalParameter($id, IQueryBuilder::PARAM_INT);
+ }, $mountIds);
+ $query = $builder->select($fields)
+ ->from($table)
+ ->where($builder->expr()->in('mount_id', $placeHolders));
+ $rows = $query->execute()->fetchAll();
+
+ $result = [];
+ foreach ($mountIds as $mountId) {
+ $result[$mountId] = [];
+ }
+ foreach ($rows as $row) {
+ if (isset($row['type'])) {
+ $row['type'] = (int)$row['type'];
+ }
+ $result[$row['mount_id']][] = $row;
+ }
+ return $result;
+ }
+
+ /**
+ * @param int[] $mountIds
+ * @return array [$id => [['type' => $type, 'value' => $value], ...], ...]
+ */
+ public function getApplicableForMounts($mountIds) {
+ return $this->selectForMounts('external_applicable', ['type', 'value'], $mountIds);
+ }
+
+ /**
+ * @param int[] $mountIds
+ * @return array [$id => ['key1' => $value1, ...], ...]
+ */
+ public function getConfigForMounts($mountIds) {
+ $mountConfigs = $this->selectForMounts('external_config', ['key', 'value'], $mountIds);
+ return array_map([$this, 'createKeyValueMap'], $mountConfigs);
+ }
+
+ /**
+ * @param int[] $mountIds
+ * @return array [$id => ['key1' => $value1, ...], ...]
+ */
+ public function getOptionsForMounts($mountIds) {
+ $mountOptions = $this->selectForMounts('external_options', ['key', 'value'], $mountIds);
+ $optionsMap = array_map([$this, 'createKeyValueMap'], $mountOptions);
+ return array_map(function (array $options) {
+ return array_map(function ($option) {
+ return json_decode($option);
+ }, $options);
+ }, $optionsMap);
+ }
+
+ /**
+ * @param array $keyValuePairs [['key'=>$key, 'value=>$value], ...]
+ * @return array ['key1' => $value1, ...]
+ */
+ private function createKeyValueMap(array $keyValuePairs) {
+ $decryptedPairts = array_map(function ($pair) {
+ if ($pair['key'] === 'password') {
+ $pair['value'] = $this->decryptValue($pair['value']);
+ }
+ return $pair;
+ }, $keyValuePairs);
+ $keys = array_map(function ($pair) {
+ return $pair['key'];
+ }, $decryptedPairts);
+ $values = array_map(function ($pair) {
+ return $pair['value'];
+ }, $decryptedPairts);
+
+ return array_combine($keys, $values);
+ }
+
+ private function encryptValue($value) {
+ return $this->crypto->encrypt($value);
+ }
+
+ private function decryptValue($value) {
+ try {
+ return $this->crypto->decrypt($value);
+ } catch (\Exception $e) {
+ return $value;
+ }
+ }
+}
diff --git a/apps/files_external/lib/Service/GlobalLegacyStoragesService.php b/apps/files_external/lib/Service/GlobalLegacyStoragesService.php
new file mode 100644
index 00000000000..4c44881051a
--- /dev/null
+++ b/apps/files_external/lib/Service/GlobalLegacyStoragesService.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+/**
+ * Read admin defined mounts from the legacy mount.json
+ */
+class GlobalLegacyStoragesService extends LegacyStoragesService {
+ /**
+ * @param BackendService $backendService
+ */
+ public function __construct(BackendService $backendService) {
+ $this->backendService = $backendService;
+ }
+
+ /**
+ * Read legacy config data
+ *
+ * @return array list of mount configs
+ */
+ protected function readLegacyConfig() {
+ // read global config
+ return \OC_Mount_Config::readData();
+ }
+}
diff --git a/apps/files_external/lib/Service/GlobalStoragesService.php b/apps/files_external/lib/Service/GlobalStoragesService.php
new file mode 100644
index 00000000000..6032a827735
--- /dev/null
+++ b/apps/files_external/lib/Service/GlobalStoragesService.php
@@ -0,0 +1,165 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use \OCP\IUserSession;
+use \OC\Files\Filesystem;
+
+use \OCA\Files_external\Lib\StorageConfig;
+use \OCA\Files_external\NotFoundException;
+
+/**
+ * Service class to manage global external storages
+ */
+class GlobalStoragesService extends StoragesService {
+ /**
+ * Triggers $signal for all applicable users of the given
+ * storage
+ *
+ * @param StorageConfig $storage storage data
+ * @param string $signal signal to trigger
+ */
+ protected function triggerHooks(StorageConfig $storage, $signal) {
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
+ $applicableUsers = $storage->getApplicableUsers();
+ $applicableGroups = $storage->getApplicableGroups();
+ if (empty($applicableUsers) && empty($applicableGroups)) {
+ // raise for user "all"
+ $this->triggerApplicableHooks(
+ $signal,
+ $storage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ ['all']
+ );
+ return;
+ }
+
+ $this->triggerApplicableHooks(
+ $signal,
+ $storage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ $applicableUsers
+ );
+ $this->triggerApplicableHooks(
+ $signal,
+ $storage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_GROUP,
+ $applicableGroups
+ );
+ }
+
+ /**
+ * Triggers signal_create_mount or signal_delete_mount to
+ * accommodate for additions/deletions in applicableUsers
+ * and applicableGroups fields.
+ *
+ * @param StorageConfig $oldStorage old storage config
+ * @param StorageConfig $newStorage new storage config
+ */
+ protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage) {
+ // if mount point changed, it's like a deletion + creation
+ if ($oldStorage->getMountPoint() !== $newStorage->getMountPoint()) {
+ $this->triggerHooks($oldStorage, Filesystem::signal_delete_mount);
+ $this->triggerHooks($newStorage, Filesystem::signal_create_mount);
+ return;
+ }
+
+ $userAdditions = array_diff($newStorage->getApplicableUsers(), $oldStorage->getApplicableUsers());
+ $userDeletions = array_diff($oldStorage->getApplicableUsers(), $newStorage->getApplicableUsers());
+ $groupAdditions = array_diff($newStorage->getApplicableGroups(), $oldStorage->getApplicableGroups());
+ $groupDeletions = array_diff($oldStorage->getApplicableGroups(), $newStorage->getApplicableGroups());
+
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
+ // if no applicable were set, raise a signal for "all"
+ $oldApplicableUsers = $oldStorage->getApplicableUsers();
+ $oldApplicableGroups = $oldStorage->getApplicableGroups();
+ if (empty($oldApplicableUsers) && empty($oldApplicableGroups)) {
+ $this->triggerApplicableHooks(
+ Filesystem::signal_delete_mount,
+ $oldStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ ['all']
+ );
+ }
+
+ // trigger delete for removed users
+ $this->triggerApplicableHooks(
+ Filesystem::signal_delete_mount,
+ $oldStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ $userDeletions
+ );
+
+ // trigger delete for removed groups
+ $this->triggerApplicableHooks(
+ Filesystem::signal_delete_mount,
+ $oldStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_GROUP,
+ $groupDeletions
+ );
+
+ // and now add the new users
+ $this->triggerApplicableHooks(
+ Filesystem::signal_create_mount,
+ $newStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ $userAdditions
+ );
+
+ // and now add the new groups
+ $this->triggerApplicableHooks(
+ Filesystem::signal_create_mount,
+ $newStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_GROUP,
+ $groupAdditions
+ );
+
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
+ // if no applicable, raise a signal for "all"
+ $newApplicableUsers = $newStorage->getApplicableUsers();
+ $newApplicableGroups = $newStorage->getApplicableGroups();
+ if (empty($newApplicableUsers) && empty($newApplicableGroups)) {
+ $this->triggerApplicableHooks(
+ Filesystem::signal_create_mount,
+ $newStorage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ ['all']
+ );
+ }
+ }
+
+ /**
+ * Get the visibility type for this controller, used in validation
+ *
+ * @return string BackendService::VISIBILITY_* constants
+ */
+ public function getVisibilityType() {
+ return BackendService::VISIBILITY_ADMIN;
+ }
+
+ protected function isApplicable(StorageConfig $config) {
+ return true;
+ }
+}
diff --git a/apps/files_external/lib/Service/ImportLegacyStoragesService.php b/apps/files_external/lib/Service/ImportLegacyStoragesService.php
new file mode 100644
index 00000000000..cb020fb495c
--- /dev/null
+++ b/apps/files_external/lib/Service/ImportLegacyStoragesService.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+class ImportLegacyStoragesService extends LegacyStoragesService {
+ private $data;
+
+ /**
+ * @param BackendService $backendService
+ */
+ public function __construct(BackendService $backendService) {
+ $this->backendService = $backendService;
+ }
+
+ public function setData($data) {
+ $this->data = $data;
+ }
+
+ /**
+ * Read legacy config data
+ *
+ * @return array list of mount configs
+ */
+ protected function readLegacyConfig() {
+ return $this->data;
+ }
+}
diff --git a/apps/files_external/lib/Service/LegacyStoragesService.php b/apps/files_external/lib/Service/LegacyStoragesService.php
new file mode 100644
index 00000000000..40f57e45506
--- /dev/null
+++ b/apps/files_external/lib/Service/LegacyStoragesService.php
@@ -0,0 +1,209 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use \OCA\Files_external\Lib\StorageConfig;
+
+/**
+ * Read mount config from legacy mount.json
+ */
+abstract class LegacyStoragesService {
+ /** @var BackendService */
+ protected $backendService;
+
+ /**
+ * Read legacy config data
+ *
+ * @return array list of mount configs
+ */
+ abstract protected function readLegacyConfig();
+
+ /**
+ * Copy legacy storage options into the given storage config object.
+ *
+ * @param StorageConfig $storageConfig storage config to populate
+ * @param string $mountType mount type
+ * @param string $applicable applicable user or group
+ * @param array $storageOptions legacy storage options
+ *
+ * @return StorageConfig populated storage config
+ */
+ protected function populateStorageConfigWithLegacyOptions(
+ &$storageConfig,
+ $mountType,
+ $applicable,
+ $storageOptions
+ ) {
+ $backend = $this->backendService->getBackend($storageOptions['backend']);
+ if (!$backend) {
+ throw new \UnexpectedValueException('Invalid backend ' . $storageOptions['backend']);
+ }
+ $storageConfig->setBackend($backend);
+ if (isset($storageOptions['authMechanism']) && $storageOptions['authMechanism'] !== 'builtin::builtin') {
+ $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 ' . $storageOptions['authMechanism']);
+ }
+ $storageConfig->setAuthMechanism($authMechanism);
+ $storageConfig->setBackendOptions($storageOptions['options']);
+ if (isset($storageOptions['mountOptions'])) {
+ $storageConfig->setMountOptions($storageOptions['mountOptions']);
+ }
+ if (!isset($storageOptions['priority'])) {
+ $storageOptions['priority'] = $backend->getPriority();
+ }
+ $storageConfig->setPriority($storageOptions['priority']);
+ if ($mountType === \OC_Mount_Config::MOUNT_TYPE_USER) {
+ $applicableUsers = $storageConfig->getApplicableUsers();
+ if ($applicable !== 'all') {
+ $applicableUsers[] = $applicable;
+ $storageConfig->setApplicableUsers($applicableUsers);
+ }
+ } else if ($mountType === \OC_Mount_Config::MOUNT_TYPE_GROUP) {
+ $applicableGroups = $storageConfig->getApplicableGroups();
+ $applicableGroups[] = $applicable;
+ $storageConfig->setApplicableGroups($applicableGroups);
+ }
+ return $storageConfig;
+ }
+
+ /**
+ * Read the external storages config
+ *
+ * @return StorageConfig[] map of storage id to storage config
+ */
+ public function getAllStorages() {
+ $mountPoints = $this->readLegacyConfig();
+ /**
+ * Here is the how the horribly messy mount point array looks like
+ * from the mount.json file:
+ *
+ * $storageOptions = $mountPoints[$mountType][$applicable][$mountPath]
+ *
+ * - $mountType is either "user" or "group"
+ * - $applicable is the name of a user or group (or the current user for personal mounts)
+ * - $mountPath is the mount point path (where the storage must be mounted)
+ * - $storageOptions is a map of storage options:
+ * - "priority": storage priority
+ * - "backend": backend identifier
+ * - "class": LEGACY backend class name
+ * - "options": backend-specific options
+ * - "authMechanism": authentication mechanism identifier
+ * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc)
+ */
+ // group by storage id
+ /** @var StorageConfig[] $storages */
+ $storages = [];
+ // for storages without id (legacy), group by config hash for
+ // later processing
+ $storagesWithConfigHash = [];
+ foreach ($mountPoints as $mountType => $applicables) {
+ foreach ($applicables as $applicable => $mountPaths) {
+ foreach ($mountPaths as $rootMountPath => $storageOptions) {
+ $currentStorage = null;
+ /**
+ * Flag whether the config that was read already has an id.
+ * If not, it will use a config hash instead and generate
+ * a proper id later
+ *
+ * @var boolean
+ */
+ $hasId = false;
+ // the root mount point is in the format "/$user/files/the/mount/point"
+ // we remove the "/$user/files" prefix
+ $parts = explode('/', ltrim($rootMountPath, '/'), 3);
+ if (count($parts) < 3) {
+ // something went wrong, skip
+ \OCP\Util::writeLog(
+ 'files_external',
+ 'Could not parse mount point "' . $rootMountPath . '"',
+ \OCP\Util::ERROR
+ );
+ continue;
+ }
+ $relativeMountPath = rtrim($parts[2], '/');
+ // note: we cannot do this after the loop because the decrypted config
+ // 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])) {
+ $currentStorage = $storages[$configId];
+ }
+ $hasId = true;
+ } else {
+ // missing id in legacy config, need to generate
+ // but at this point we don't know the max-id, so use
+ // first group it by config hash
+ $storageOptions['mountpoint'] = $rootMountPath;
+ $configId = \OC_Mount_Config::makeConfigHash($storageOptions);
+ if (isset($storagesWithConfigHash[$configId])) {
+ $currentStorage = $storagesWithConfigHash[$configId];
+ }
+ }
+ if (is_null($currentStorage)) {
+ // create new
+ $currentStorage = new StorageConfig($configId);
+ $currentStorage->setMountPoint($relativeMountPath);
+ }
+ try {
+ $this->populateStorageConfigWithLegacyOptions(
+ $currentStorage,
+ $mountType,
+ $applicable,
+ $storageOptions
+ );
+ if ($hasId) {
+ $storages[$configId] = $currentStorage;
+ } else {
+ $storagesWithConfigHash[$configId] = $currentStorage;
+ }
+ } catch (\UnexpectedValueException $e) {
+ // don't die if a storage backend doesn't exist
+ \OCP\Util::writeLog(
+ 'files_external',
+ 'Could not load storage: "' . $e->getMessage() . '"',
+ \OCP\Util::ERROR
+ );
+ }
+ }
+ }
+ }
+
+ // convert parameter values
+ foreach ($storages as $storage) {
+ $storage->getBackend()->validateStorageDefinition($storage);
+ $storage->getAuthMechanism()->validateStorageDefinition($storage);
+ }
+ return $storages;
+ }
+}
diff --git a/apps/files_external/lib/Service/StoragesService.php b/apps/files_external/lib/Service/StoragesService.php
new file mode 100644
index 00000000000..0999c0a7adf
--- /dev/null
+++ b/apps/files_external/lib/Service/StoragesService.php
@@ -0,0 +1,527 @@
+<?php
+/**
+ * @author Jesús Macias <jmacias@solidgear.es>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use \OC\Files\Filesystem;
+use \OCA\Files_external\Lib\StorageConfig;
+use \OCA\Files_external\NotFoundException;
+use \OCA\Files_External\Lib\Backend\Backend;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCP\Files\Config\IUserMountCache;
+use \OCP\Files\StorageNotAvailableException;
+
+/**
+ * Service class to manage external storages
+ */
+abstract class StoragesService {
+
+ /** @var BackendService */
+ protected $backendService;
+
+ /**
+ * @var DBConfigService
+ */
+ protected $dbConfig;
+
+ /**
+ * @var IUserMountCache
+ */
+ protected $userMountCache;
+
+ /**
+ * @param BackendService $backendService
+ * @param DBConfigService $dbConfigService
+ * @param IUserMountCache $userMountCache
+ */
+ public function __construct(BackendService $backendService, DBConfigService $dbConfigService, IUserMountCache $userMountCache) {
+ $this->backendService = $backendService;
+ $this->dbConfig = $dbConfigService;
+ $this->userMountCache = $userMountCache;
+ }
+
+ protected function readDBConfig() {
+ return $this->dbConfig->getAdminMounts();
+ }
+
+ protected function getStorageConfigFromDBMount(array $mount) {
+ $applicableUsers = array_filter($mount['applicable'], function ($applicable) {
+ return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER;
+ });
+ $applicableUsers = array_map(function ($applicable) {
+ return $applicable['value'];
+ }, $applicableUsers);
+
+ $applicableGroups = array_filter($mount['applicable'], function ($applicable) {
+ return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP;
+ });
+ $applicableGroups = array_map(function ($applicable) {
+ return $applicable['value'];
+ }, $applicableGroups);
+
+ try {
+ $config = $this->createStorage(
+ $mount['mount_point'],
+ $mount['storage_backend'],
+ $mount['auth_backend'],
+ $mount['config'],
+ $mount['options'],
+ array_values($applicableUsers),
+ array_values($applicableGroups),
+ $mount['priority']
+ );
+ $config->setType($mount['type']);
+ $config->setId((int)$mount['mount_id']);
+ return $config;
+ } catch (\UnexpectedValueException $e) {
+ // don't die if a storage backend doesn't exist
+ \OCP\Util::writeLog(
+ 'files_external',
+ 'Could not load storage: "' . $e->getMessage() . '"',
+ \OCP\Util::ERROR
+ );
+ return null;
+ } catch (\InvalidArgumentException $e) {
+ \OCP\Util::writeLog(
+ 'files_external',
+ 'Could not load storage: "' . $e->getMessage() . '"',
+ \OCP\Util::ERROR
+ );
+ return null;
+ }
+ }
+
+ /**
+ * Read the external storages config
+ *
+ * @return array map of storage id to storage config
+ */
+ protected function readConfig() {
+ $mounts = $this->readDBConfig();
+ $configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
+ $configs = array_filter($configs, function ($config) {
+ return $config instanceof StorageConfig;
+ });
+
+ $keys = array_map(function (StorageConfig $config) {
+ return $config->getId();
+ }, $configs);
+
+ return array_combine($keys, $configs);
+ }
+
+ /**
+ * Get a storage with status
+ *
+ * @param int $id storage id
+ *
+ * @return StorageConfig
+ * @throws NotFoundException if the storage with the given id was not found
+ */
+ public function getStorage($id) {
+ $mount = $this->dbConfig->getMountById($id);
+
+ if (!is_array($mount)) {
+ throw new NotFoundException('Storage with id "' . $id . '" not found');
+ }
+
+ $config = $this->getStorageConfigFromDBMount($mount);
+ if ($this->isApplicable($config)) {
+ return $config;
+ } else {
+ throw new NotFoundException('Storage with id "' . $id . '" not found');
+ }
+ }
+
+ /**
+ * Check whether this storage service should provide access to a storage
+ *
+ * @param StorageConfig $config
+ * @return bool
+ */
+ abstract protected function isApplicable(StorageConfig $config);
+
+ /**
+ * Gets all storages, valid or not
+ *
+ * @return StorageConfig[] array of storage configs
+ */
+ public function getAllStorages() {
+ return $this->readConfig();
+ }
+
+ /**
+ * Gets all valid storages
+ *
+ * @return StorageConfig[]
+ */
+ public function getStorages() {
+ return array_filter($this->getAllStorages(), [$this, 'validateStorage']);
+ }
+
+ /**
+ * Validate storage
+ * FIXME: De-duplicate with StoragesController::validate()
+ *
+ * @param StorageConfig $storage
+ * @return bool
+ */
+ protected function validateStorage(StorageConfig $storage) {
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+
+ if (!$backend->isVisibleFor($this->getVisibilityType())) {
+ // not permitted to use backend
+ return false;
+ }
+ if (!$authMechanism->isVisibleFor($this->getVisibilityType())) {
+ // not permitted to use auth mechanism
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the visibility type for this controller, used in validation
+ *
+ * @return string BackendService::VISIBILITY_* constants
+ */
+ abstract public function getVisibilityType();
+
+ /**
+ * @return integer
+ */
+ protected function getType() {
+ return DBConfigService::MOUNT_TYPE_ADMIN;
+ }
+
+ /**
+ * Add new storage to the configuration
+ *
+ * @param StorageConfig $newStorage storage attributes
+ *
+ * @return StorageConfig storage config, with added id
+ */
+ public function addStorage(StorageConfig $newStorage) {
+ $allStorages = $this->readConfig();
+
+ $configId = $this->dbConfig->addMount(
+ $newStorage->getMountPoint(),
+ $newStorage->getBackend()->getIdentifier(),
+ $newStorage->getAuthMechanism()->getIdentifier(),
+ $newStorage->getPriority(),
+ $this->getType()
+ );
+
+ $newStorage->setId($configId);
+
+ foreach ($newStorage->getApplicableUsers() as $user) {
+ $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_USER, $user);
+ }
+ foreach ($newStorage->getApplicableGroups() as $group) {
+ $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
+ }
+ foreach ($newStorage->getBackendOptions() as $key => $value) {
+ $this->dbConfig->setConfig($configId, $key, $value);
+ }
+ foreach ($newStorage->getMountOptions() as $key => $value) {
+ $this->dbConfig->setOption($configId, $key, $value);
+ }
+
+ if (count($newStorage->getApplicableUsers()) === 0 && count($newStorage->getApplicableGroups()) === 0) {
+ $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
+ }
+
+ // add new storage
+ $allStorages[$configId] = $newStorage;
+
+ $this->triggerHooks($newStorage, Filesystem::signal_create_mount);
+
+ $newStorage->setStatus(StorageNotAvailableException::STATUS_SUCCESS);
+ return $newStorage;
+ }
+
+ /**
+ * Create a storage from its parameters
+ *
+ * @param string $mountPoint storage mount point
+ * @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
+ * @param array|null $applicableGroups groups for which to mount the storage
+ * @param int|null $priority priority
+ *
+ * @return StorageConfig
+ */
+ public function createStorage(
+ $mountPoint,
+ $backendIdentifier,
+ $authMechanismIdentifier,
+ $backendOptions,
+ $mountOptions = null,
+ $applicableUsers = null,
+ $applicableGroups = null,
+ $priority = null
+ ) {
+ $backend = $this->backendService->getBackend($backendIdentifier);
+ if (!$backend) {
+ throw new \InvalidArgumentException('Unable to get backend for ' . $backendIdentifier);
+ }
+ $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier);
+ if (!$authMechanism) {
+ throw new \InvalidArgumentException('Unable to get authentication mechanism for ' . $authMechanismIdentifier);
+ }
+ $newStorage = new StorageConfig();
+ $newStorage->setMountPoint($mountPoint);
+ $newStorage->setBackend($backend);
+ $newStorage->setAuthMechanism($authMechanism);
+ $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
+ *
+ * @param string $signal signal
+ * @param string $mountPoint hook mount pount param
+ * @param string $mountType hook mount type param
+ * @param array $applicableArray array of applicable users/groups for which to trigger the hook
+ */
+ protected function triggerApplicableHooks($signal, $mountPoint, $mountType, $applicableArray) {
+ foreach ($applicableArray as $applicable) {
+ \OCP\Util::emitHook(
+ Filesystem::CLASSNAME,
+ $signal,
+ [
+ Filesystem::signal_param_path => $mountPoint,
+ Filesystem::signal_param_mount_type => $mountType,
+ Filesystem::signal_param_users => $applicable,
+ ]
+ );
+ }
+ }
+
+ /**
+ * Triggers $signal for all applicable users of the given
+ * storage
+ *
+ * @param StorageConfig $storage storage data
+ * @param string $signal signal to trigger
+ */
+ abstract protected function triggerHooks(StorageConfig $storage, $signal);
+
+ /**
+ * Triggers signal_create_mount or signal_delete_mount to
+ * accommodate for additions/deletions in applicableUsers
+ * and applicableGroups fields.
+ *
+ * @param StorageConfig $oldStorage old storage data
+ * @param StorageConfig $newStorage new storage data
+ */
+ abstract protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage);
+
+ /**
+ * Update storage to the configuration
+ *
+ * @param StorageConfig $updatedStorage storage attributes
+ *
+ * @return StorageConfig storage config
+ * @throws NotFoundException if the given storage does not exist in the config
+ */
+ public function updateStorage(StorageConfig $updatedStorage) {
+ $id = $updatedStorage->getId();
+
+ $existingMount = $this->dbConfig->getMountById($id);
+
+ if (!is_array($existingMount)) {
+ throw new NotFoundException('Storage with id "' . $id . '" not found while updating storage');
+ }
+
+ $oldStorage = $this->getStorageConfigFromDBMount($existingMount);
+
+ $removedUsers = array_diff($oldStorage->getApplicableUsers(), $updatedStorage->getApplicableUsers());
+ $removedGroups = array_diff($oldStorage->getApplicableGroups(), $updatedStorage->getApplicableGroups());
+ $addedUsers = array_diff($updatedStorage->getApplicableUsers(), $oldStorage->getApplicableUsers());
+ $addedGroups = array_diff($updatedStorage->getApplicableGroups(), $oldStorage->getApplicableGroups());
+
+ $oldUserCount = count($oldStorage->getApplicableUsers());
+ $oldGroupCount = count($oldStorage->getApplicableGroups());
+ $newUserCount = count($updatedStorage->getApplicableUsers());
+ $newGroupCount = count($updatedStorage->getApplicableGroups());
+ $wasGlobal = ($oldUserCount + $oldGroupCount) === 0;
+ $isGlobal = ($newUserCount + $newGroupCount) === 0;
+
+ foreach ($removedUsers as $user) {
+ $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
+ }
+ foreach ($removedGroups as $group) {
+ $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
+ }
+ foreach ($addedUsers as $user) {
+ $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
+ }
+ foreach ($addedGroups as $group) {
+ $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
+ }
+
+ if ($wasGlobal && !$isGlobal) {
+ $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
+ } else if (!$wasGlobal && $isGlobal) {
+ $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
+ }
+
+ $changedConfig = array_diff_assoc($updatedStorage->getBackendOptions(), $oldStorage->getBackendOptions());
+ $changedOptions = array_diff_assoc($updatedStorage->getMountOptions(), $oldStorage->getMountOptions());
+
+ foreach ($changedConfig as $key => $value) {
+ $this->dbConfig->setConfig($id, $key, $value);
+ }
+ foreach ($changedOptions as $key => $value) {
+ $this->dbConfig->setOption($id, $key, $value);
+ }
+
+ if ($updatedStorage->getMountPoint() !== $oldStorage->getMountPoint()) {
+ $this->dbConfig->setMountPoint($id, $updatedStorage->getMountPoint());
+ }
+
+ if ($updatedStorage->getAuthMechanism()->getIdentifier() !== $oldStorage->getAuthMechanism()->getIdentifier()) {
+ $this->dbConfig->setAuthBackend($id, $updatedStorage->getAuthMechanism()->getIdentifier());
+ }
+
+ $this->triggerChangeHooks($oldStorage, $updatedStorage);
+
+ if (($wasGlobal && !$isGlobal) || count($removedGroups) > 0) { // to expensive to properly handle these on the fly
+ $this->userMountCache->remoteStorageMounts($this->getStorageId($updatedStorage));
+ } else {
+ $storageId = $this->getStorageId($updatedStorage);
+ foreach ($removedUsers as $userId) {
+ $this->userMountCache->removeUserStorageMount($storageId, $userId);
+ }
+ }
+
+ return $this->getStorage($id);
+ }
+
+ /**
+ * Delete the storage with the given id.
+ *
+ * @param int $id storage id
+ *
+ * @throws NotFoundException if no storage was found with the given id
+ */
+ public function removeStorage($id) {
+ $existingMount = $this->dbConfig->getMountById($id);
+
+ if (!is_array($existingMount)) {
+ throw new NotFoundException('Storage with id "' . $id . '" not found');
+ }
+
+ $this->dbConfig->removeMount($id);
+
+ $deletedStorage = $this->getStorageConfigFromDBMount($existingMount);
+ $this->triggerHooks($deletedStorage, Filesystem::signal_delete_mount);
+
+ // delete oc_storages entries and oc_filecache
+ try {
+ $rustyStorageId = $this->getRustyStorageIdFromConfig($deletedStorage);
+ \OC\Files\Cache\Storage::remove($rustyStorageId);
+ } catch (\Exception $e) {
+ // can happen either for invalid configs where the storage could not
+ // be instantiated or whenever $user vars where used, in which case
+ // the storage id could not be computed
+ \OCP\Util::writeLog(
+ 'files_external',
+ 'Exception: "' . $e->getMessage() . '"',
+ \OCP\Util::ERROR
+ );
+ }
+ }
+
+ /**
+ * Returns the rusty storage id from oc_storages from the given storage config.
+ *
+ * @param StorageConfig $storageConfig
+ * @return string rusty storage id
+ */
+ private function getRustyStorageIdFromConfig(StorageConfig $storageConfig) {
+ // if any of the storage options contains $user, it is not possible
+ // to compute the possible storage id as we don't know which users
+ // mounted it already (and we certainly don't want to iterate over ALL users)
+ foreach ($storageConfig->getBackendOptions() as $value) {
+ if (strpos($value, '$user') !== false) {
+ throw new \Exception('Cannot compute storage id for deletion due to $user vars in the configuration');
+ }
+ }
+
+ // note: similar to ConfigAdapter->prepateStorageConfig()
+ $storageConfig->getAuthMechanism()->manipulateStorageConfig($storageConfig);
+ $storageConfig->getBackend()->manipulateStorageConfig($storageConfig);
+
+ $class = $storageConfig->getBackend()->getStorageClass();
+ $storageImpl = new $class($storageConfig->getBackendOptions());
+
+ return $storageImpl->getId();
+ }
+
+ /**
+ * Construct the storage implementation
+ *
+ * @param StorageConfig $storageConfig
+ * @return int
+ */
+ private function getStorageId(StorageConfig $storageConfig) {
+ try {
+ $class = $storageConfig->getBackend()->getStorageClass();
+ /** @var \OC\Files\Storage\Storage $storage */
+ $storage = new $class($storageConfig->getBackendOptions());
+
+ // auth mechanism should fire first
+ $storage = $storageConfig->getBackend()->wrapStorage($storage);
+ $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
+
+ return $storage->getStorageCache()->getNumericId();
+ } catch (\Exception $e) {
+ return -1;
+ }
+ }
+}
diff --git a/apps/files_external/lib/Service/UserGlobalStoragesService.php b/apps/files_external/lib/Service/UserGlobalStoragesService.php
new file mode 100644
index 00000000000..b5e2d13c712
--- /dev/null
+++ b/apps/files_external/lib/Service/UserGlobalStoragesService.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use OCP\Files\Config\IUserMountCache;
+use \OCP\IUserSession;
+use \OCP\IGroupManager;
+use \OCA\Files_External\Lib\StorageConfig;
+
+/**
+ * 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 DBConfigService $dbConfig
+ * @param IUserSession $userSession
+ * @param IGroupManager $groupManager
+ * @param IUserMountCache $userMountCache
+ */
+ public function __construct(
+ BackendService $backendService,
+ DBConfigService $dbConfig,
+ IUserSession $userSession,
+ IGroupManager $groupManager,
+ IUserMountCache $userMountCache
+ ) {
+ parent::__construct($backendService, $dbConfig, $userMountCache);
+ $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;
+ }
+ }
+
+ protected function readDBConfig() {
+ $userMounts = $this->dbConfig->getAdminMountsFor(DBConfigService::APPLICABLE_TYPE_USER, $this->getUser()->getUID());
+ $globalMounts = $this->dbConfig->getAdminMountsFor(DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
+ $groups = $this->groupManager->getUserGroupIds($this->getUser());
+ if (is_array($groups) && count($groups) !== 0) {
+ $groupMounts = $this->dbConfig->getAdminMountsForMultiple(DBConfigService::APPLICABLE_TYPE_GROUP, $groups);
+ } else {
+ $groupMounts = [];
+ }
+ return array_merge($userMounts, $groupMounts, $globalMounts);
+ }
+
+ public function addStorage(StorageConfig $newStorage) {
+ throw new \DomainException('UserGlobalStoragesService writing disallowed');
+ }
+
+ public function updateStorage(StorageConfig $updatedStorage) {
+ throw new \DomainException('UserGlobalStoragesService writing disallowed');
+ }
+
+ /**
+ * @param integer $id
+ */
+ public function removeStorage($id) {
+ throw new \DomainException('UserGlobalStoragesService writing disallowed');
+ }
+
+ /**
+ * Get unique storages, in case two are defined with the same mountpoint
+ * Higher priority storages take precedence
+ *
+ * @return StorageConfig[]
+ */
+ public function getUniqueStorages() {
+ $storages = $this->getStorages();
+
+ $storagesByMountpoint = [];
+ foreach ($storages as $storage) {
+ $storagesByMountpoint[$storage->getMountPoint()][] = $storage;
+ }
+
+ $result = [];
+ foreach ($storagesByMountpoint as $storageList) {
+ $storage = array_reduce($storageList, function ($carry, $item) {
+ if (isset($carry)) {
+ $carryPriorityType = $this->getPriorityType($carry);
+ $itemPriorityType = $this->getPriorityType($item);
+ if ($carryPriorityType > $itemPriorityType) {
+ return $carry;
+ } elseif ($carryPriorityType === $itemPriorityType) {
+ if ($carry->getPriority() > $item->getPriority()) {
+ return $carry;
+ }
+ }
+ }
+ return $item;
+ });
+ $result[$storage->getID()] = $storage;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get a priority 'type', where a bigger number means higher priority
+ * user applicable > group applicable > 'all'
+ *
+ * @param StorageConfig $storage
+ * @return int
+ */
+ protected function getPriorityType(StorageConfig $storage) {
+ $applicableUsers = $storage->getApplicableUsers();
+ $applicableGroups = $storage->getApplicableGroups();
+
+ if ($applicableUsers && $applicableUsers[0] !== 'all') {
+ return 2;
+ }
+ if ($applicableGroups) {
+ return 1;
+ }
+ return 0;
+ }
+
+ protected function isApplicable(StorageConfig $config) {
+ $applicableUsers = $config->getApplicableUsers();
+ $applicableGroups = $config->getApplicableGroups();
+
+ if (count($applicableUsers) === 0 && count($applicableGroups) === 0) {
+ return true;
+ }
+ if (in_array($this->getUser()->getUID(), $applicableUsers, true)) {
+ return true;
+ }
+ $groupIds = $this->groupManager->getUserGroupIds($this->getUser());
+ foreach ($groupIds as $groupId) {
+ if (in_array($groupId, $applicableGroups, true)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/apps/files_external/lib/Service/UserLegacyStoragesService.php b/apps/files_external/lib/Service/UserLegacyStoragesService.php
new file mode 100644
index 00000000000..c64520c8b02
--- /dev/null
+++ b/apps/files_external/lib/Service/UserLegacyStoragesService.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use OCP\IUserSession;
+
+/**
+ * Read user defined mounts from the legacy mount.json
+ */
+class UserLegacyStoragesService extends LegacyStoragesService {
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * @param BackendService $backendService
+ * @param IUserSession $userSession
+ */
+ public function __construct(BackendService $backendService, IUserSession $userSession) {
+ $this->backendService = $backendService;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * Read legacy config data
+ *
+ * @return array list of storage configs
+ */
+ protected function readLegacyConfig() {
+ // read user config
+ $user = $this->userSession->getUser()->getUID();
+ return \OC_Mount_Config::readData($user);
+ }
+}
diff --git a/apps/files_external/lib/Service/UserStoragesService.php b/apps/files_external/lib/Service/UserStoragesService.php
new file mode 100644
index 00000000000..b64f289b3ee
--- /dev/null
+++ b/apps/files_external/lib/Service/UserStoragesService.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_External\Service;
+
+use OCP\Files\Config\IUserMountCache;
+use \OCP\IUserSession;
+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
+ * (aka personal storages)
+ */
+class UserStoragesService extends StoragesService {
+ use UserTrait;
+
+ /**
+ * Create a user storages service
+ *
+ * @param BackendService $backendService
+ * @param DBConfigService $dbConfig
+ * @param IUserSession $userSession user session
+ * @param IUserMountCache $userMountCache
+ */
+ public function __construct(
+ BackendService $backendService,
+ DBConfigService $dbConfig,
+ IUserSession $userSession,
+ IUserMountCache $userMountCache
+ ) {
+ $this->userSession = $userSession;
+ parent::__construct($backendService, $dbConfig, $userMountCache);
+ }
+
+ protected function readDBConfig() {
+ return $this->dbConfig->getUserMountsFor(DBConfigService::APPLICABLE_TYPE_USER, $this->getUser()->getUID());
+ }
+
+ /**
+ * Triggers $signal for all applicable users of the given
+ * storage
+ *
+ * @param StorageConfig $storage storage data
+ * @param string $signal signal to trigger
+ */
+ protected function triggerHooks(StorageConfig $storage, $signal) {
+ $user = $this->getUser()->getUID();
+
+ // trigger hook for the current user
+ $this->triggerApplicableHooks(
+ $signal,
+ $storage->getMountPoint(),
+ \OC_Mount_Config::MOUNT_TYPE_USER,
+ [$user]
+ );
+ }
+
+ /**
+ * Triggers signal_create_mount or signal_delete_mount to
+ * accommodate for additions/deletions in applicableUsers
+ * and applicableGroups fields.
+ *
+ * @param StorageConfig $oldStorage old storage data
+ * @param StorageConfig $newStorage new storage data
+ */
+ protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage) {
+ // if mount point changed, it's like a deletion + creation
+ if ($oldStorage->getMountPoint() !== $newStorage->getMountPoint()) {
+ $this->triggerHooks($oldStorage, Filesystem::signal_delete_mount);
+ $this->triggerHooks($newStorage, Filesystem::signal_create_mount);
+ }
+ }
+
+ protected function getType() {
+ return DBConfigService::MOUNT_TYPE_PERSONAl;
+ }
+
+ /**
+ * Add new storage to the configuration
+ *
+ * @param StorageConfig $newStorage storage attributes
+ *
+ * @return StorageConfig storage config, with added id
+ */
+ public function addStorage(StorageConfig $newStorage) {
+ $newStorage->setApplicableUsers([$this->getUser()->getUID()]);
+ $config = parent::addStorage($newStorage);
+ return $config;
+ }
+
+ /**
+ * Update storage to the configuration
+ *
+ * @param StorageConfig $updatedStorage storage attributes
+ *
+ * @return StorageConfig storage config
+ * @throws NotFoundException if the given storage does not exist in the config
+ */
+ public function updateStorage(StorageConfig $updatedStorage) {
+ $updatedStorage->setApplicableUsers([$this->getUser()->getUID()]);
+ return parent::updateStorage($updatedStorage);
+ }
+
+ /**
+ * Get the visibility type for this controller, used in validation
+ *
+ * @return string BackendService::VISIBILITY_* constants
+ */
+ public function getVisibilityType() {
+ return BackendService::VISIBILITY_PERSONAL;
+ }
+
+ protected function isApplicable(StorageConfig $config) {
+ return ($config->getApplicableUsers() === [$this->getUser()->getUID()]) && $config->getType() === StorageConfig::MOUNT_TYPE_PERSONAl;
+ }
+}
diff --git a/apps/files_external/lib/Service/UserTrait.php b/apps/files_external/lib/Service/UserTrait.php
new file mode 100644
index 00000000000..536c0f67e1f
--- /dev/null
+++ b/apps/files_external/lib/Service/UserTrait.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+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;
+ }
+}
+
diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php
index a19a111d3d9..a05ff555f72 100644
--- a/apps/files_external/lib/config/configadapter.php
+++ b/apps/files_external/lib/config/configadapter.php
@@ -24,14 +24,14 @@
namespace OCA\Files_External\Config;
use OC\Files\Storage\Wrapper\Availability;
-use OCA\Files_external\Migration\StorageMigrator;
+use OCA\Files_External\Migration\StorageMigrator;
use OCP\Files\Storage;
use OC\Files\Mount\MountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCA\Files_External\Lib\PersonalMount;
use OCP\Files\Config\IMountProvider;
use OCP\IUser;
-use OCA\Files_external\Service\UserStoragesService;
+use OCA\Files_External\Service\UserStoragesService;
use OCA\Files_External\Service\UserGlobalStoragesService;
use OCA\Files_External\Lib\StorageConfig;
use OC\Files\Storage\FailedStorage;