diff --git a/apps/files_external/ajax/dropbox.php b/apps/files_external/ajax/oauth1.php similarity index 89% rename from apps/files_external/ajax/dropbox.php rename to apps/files_external/ajax/oauth1.php index 55dc417b73a3..ca339aeec584 100644 --- a/apps/files_external/ajax/dropbox.php +++ b/apps/files_external/ajax/oauth1.php @@ -30,6 +30,7 @@ OCP\JSON::callCheck(); $l = \OC::$server->getL10N('files_external'); +// FIXME: currently hard-coded to Dropbox OAuth if (isset($_POST['app_key']) && isset($_POST['app_secret'])) { $oauth = new Dropbox_OAuth_Curl((string)$_POST['app_key'], (string)$_POST['app_secret']); if (isset($_POST['step'])) { @@ -47,7 +48,7 @@ 'request_token_secret' => $token['token_secret']))); } catch (Exception $exception) { OCP\JSON::error(array('data' => array('message' => - $l->t('Fetching request tokens failed. Verify that your Dropbox app key and secret are correct.')) + $l->t('Fetching request tokens failed. Verify that your app key and secret are correct.')) )); } break; @@ -60,7 +61,7 @@ 'access_token_secret' => $token['token_secret'])); } catch (Exception $exception) { OCP\JSON::error(array('data' => array('message' => - $l->t('Fetching access tokens failed. Verify that your Dropbox app key and secret are correct.')) + $l->t('Fetching access tokens failed. Verify that your app key and secret are correct.')) )); } } @@ -68,5 +69,5 @@ } } } else { - OCP\JSON::error(array('data' => array('message' => $l->t('Please provide a valid Dropbox app key and secret.')))); + OCP\JSON::error(array('data' => array('message' => $l->t('Please provide a valid app key and secret.')))); } diff --git a/apps/files_external/ajax/google.php b/apps/files_external/ajax/oauth2.php similarity index 98% rename from apps/files_external/ajax/google.php rename to apps/files_external/ajax/oauth2.php index acaf1b0b27f2..0a202e3ddcb2 100644 --- a/apps/files_external/ajax/google.php +++ b/apps/files_external/ajax/oauth2.php @@ -33,6 +33,7 @@ OCP\JSON::callCheck(); $l = \OC::$server->getL10N('files_external'); +// FIXME: currently hard-coded to Google Drive if (isset($_POST['client_id']) && isset($_POST['client_secret']) && isset($_POST['redirect'])) { $client = new Google_Client(); $client->setClientId((string)$_POST['client_id']); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 66897eba3d36..bdcaff64c8b1 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -30,9 +30,6 @@ * along with this program. If not, see * */ -$app = new \OCA\Files_external\Appinfo\Application(); - -$l = \OC::$server->getL10N('files_external'); OC::$CLASSPATH['OC\Files\Storage\StreamWrapper'] = 'files_external/lib/streamwrapper.php'; OC::$CLASSPATH['OC\Files\Storage\FTP'] = 'files_external/lib/ftp.php'; @@ -40,16 +37,19 @@ OC::$CLASSPATH['OC\Files\Storage\Google'] = 'files_external/lib/google.php'; OC::$CLASSPATH['OC\Files\Storage\Swift'] = 'files_external/lib/swift.php'; OC::$CLASSPATH['OC\Files\Storage\SMB'] = 'files_external/lib/smb.php'; -OC::$CLASSPATH['OC\Files\Storage\SMB_OC'] = 'files_external/lib/smb_oc.php'; OC::$CLASSPATH['OC\Files\Storage\AmazonS3'] = 'files_external/lib/amazons3.php'; OC::$CLASSPATH['OC\Files\Storage\Dropbox'] = 'files_external/lib/dropbox.php'; OC::$CLASSPATH['OC\Files\Storage\SFTP'] = 'files_external/lib/sftp.php'; -OC::$CLASSPATH['OC\Files\Storage\SFTP_Key'] = 'files_external/lib/sftp_key.php'; OC::$CLASSPATH['OC_Mount_Config'] = 'files_external/lib/config.php'; OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php'; require_once __DIR__ . '/../3rdparty/autoload.php'; +$app = new \OCA\Files_external\Appinfo\Application(); +$appContainer = $app->getContainer(); + +$l = \OC::$server->getL10N('files_external'); + OCP\App::registerAdmin('files_external', 'settings'); if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') { OCP\App::registerPersonal('files_external', 'personal'); @@ -63,179 +63,12 @@ "name" => $l->t('External storage') ]); +// Teach OC_Mount_Config about the AppFramework +\OC_Mount_Config::initApp($appContainer); + // connecting hooks OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook'); OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login'); -OC_Mount_Config::registerBackend('\OC\Files\Storage\Local', [ - 'backend' => (string)$l->t('Local'), - 'priority' => 150, - 'configuration' => [ - 'datadir' => (string)$l->t('Location') - ], -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', [ - 'backend' => (string)$l->t('Amazon S3'), - 'priority' => 100, - 'configuration' => [ - 'key' => (string)$l->t('Key'), - 'secret' => '*'.$l->t('Secret'), - 'bucket' => (string)$l->t('Bucket'), - ], - 'has_dependencies' => true, -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', [ - 'backend' => (string)$l->t('Amazon S3 and compliant'), - 'priority' => 100, - 'configuration' => [ - 'key' => (string)$l->t('Access Key'), - 'secret' => '*'.$l->t('Secret Key'), - 'bucket' => (string)$l->t('Bucket'), - 'hostname' => '&'.$l->t('Hostname'), - 'port' => '&'.$l->t('Port'), - 'region' => '&'.$l->t('Region'), - 'use_ssl' => '!'.$l->t('Enable SSL'), - 'use_path_style' => '!'.$l->t('Enable Path Style') - ], - 'has_dependencies' => true, -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\Dropbox', [ - 'backend' => 'Dropbox', - 'priority' => 100, - 'configuration' => [ - 'configured' => '#configured', - 'app_key' => (string)$l->t('App key'), - 'app_secret' => '*'.$l->t('App secret'), - 'token' => '#token', - 'token_secret' => '#token_secret' - ], - 'custom' => 'dropbox', - 'has_dependencies' => true, -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\FTP', [ - 'backend' => 'FTP', - 'priority' => 100, - 'configuration' => [ - 'host' => (string)$l->t('Host'), - 'user' => (string)$l->t('Username'), - 'password' => '*'.$l->t('Password'), - 'root' => '&'.$l->t('Remote subfolder'), - 'secure' => '!'.$l->t('Secure ftps://') - ], - 'has_dependencies' => true, -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\Google', [ - 'backend' => 'Google Drive', - 'priority' => 100, - 'configuration' => [ - 'configured' => '#configured', - 'client_id' => (string)$l->t('Client ID'), - 'client_secret' => '*'.$l->t('Client secret'), - 'token' => '#token', - ], - 'custom' => 'google', - 'has_dependencies' => true, -]); - - -OC_Mount_Config::registerBackend('\OC\Files\Storage\Swift', [ - 'backend' => (string)$l->t('OpenStack Object Storage'), - 'priority' => 100, - 'configuration' => [ - 'user' => (string)$l->t('Username'), - 'bucket' => (string)$l->t('Bucket'), - 'region' => '&'.$l->t('Region (optional for OpenStack Object Storage)'), - 'key' => '&*'.$l->t('API Key (required for Rackspace Cloud Files)'), - 'tenant' => '&'.$l->t('Tenantname (required for OpenStack Object Storage)'), - 'password' => '&*'.$l->t('Password (required for OpenStack Object Storage)'), - 'service_name' => '&'.$l->t('Service Name (required for OpenStack Object Storage)'), - 'url' => '&'.$l->t('URL of identity endpoint (required for OpenStack Object Storage)'), - 'timeout' => '&'.$l->t('Timeout of HTTP requests in seconds'), - ], - 'has_dependencies' => true, -]); - - -if (!OC_Util::runningOnWindows()) { - OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB', [ - 'backend' => 'SMB / CIFS', - 'priority' => 100, - 'configuration' => [ - 'host' => (string)$l->t('Host'), - 'user' => (string)$l->t('Username'), - 'password' => '*'.$l->t('Password'), - 'share' => (string)$l->t('Share'), - 'root' => '&'.$l->t('Remote subfolder'), - ], - 'has_dependencies' => true, - ]); - - OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB_OC', [ - 'backend' => (string)$l->t('SMB / CIFS using OC login'), - 'priority' => 90, - 'configuration' => [ - 'host' => (string)$l->t('Host'), - 'username_as_share' => '!'.$l->t('Username as share'), - 'share' => '&'.$l->t('Share'), - 'root' => '&'.$l->t('Remote subfolder'), - ], - 'has_dependencies' => true, - ]); -} - -OC_Mount_Config::registerBackend('\OC\Files\Storage\DAV', [ - 'backend' => 'WebDAV', - 'priority' => 100, - 'configuration' => [ - 'host' => (string)$l->t('URL'), - 'user' => (string)$l->t('Username'), - 'password' => '*'.$l->t('Password'), - 'root' => '&'.$l->t('Remote subfolder'), - 'secure' => '!'.$l->t('Secure https://'), - ], - 'has_dependencies' => true, -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\OwnCloud', [ - 'backend' => 'ownCloud', - 'priority' => 100, - 'configuration' => [ - 'host' => (string)$l->t('URL'), - 'user' => (string)$l->t('Username'), - 'password' => '*'.$l->t('Password'), - 'root' => '&'.$l->t('Remote subfolder'), - 'secure' => '!'.$l->t('Secure https://'), - ], -]); - - -OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP', [ - 'backend' => 'SFTP', - 'priority' => 100, - 'configuration' => [ - 'host' => (string)$l->t('Host'), - 'user' => (string)$l->t('Username'), - 'password' => '*'.$l->t('Password'), - 'root' => '&'.$l->t('Remote subfolder'), - ], -]); - -OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP_Key', [ - 'backend' => (string)$l->t('SFTP with secret key login'), - 'priority' => 100, - 'configuration' => array( - 'host' => (string)$l->t('Host'), - 'user' => (string)$l->t('Username'), - 'public_key' => (string)$l->t('Public key'), - 'private_key' => '#private_key', - 'root' => '&'.$l->t('Remote subfolder')), - 'custom' => 'sftp_key', - ] -); -$mountProvider = new \OCA\Files_External\Config\ConfigAdapter(); +$mountProvider = $appContainer->query('OCA\Files_External\Config\ConfigAdapter'); \OC::$server->getMountProviderCollection()->registerProvider($mountProvider); diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php index d77a302466c9..de2bb64d287f 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -3,6 +3,7 @@ * @author Morris Jobke * @author Ross Nicoll * @author Vincent Petry + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -23,9 +24,9 @@ namespace OCA\Files_External\AppInfo; -use \OCA\Files_External\Controller\AjaxController; use \OCP\AppFramework\App; use \OCP\IContainer; +use \OCA\Files_External\Service\BackendService; /** * @package OCA\Files_External\Appinfo @@ -34,16 +35,72 @@ class Application extends App { public function __construct(array $urlParams=array()) { parent::__construct('files_external', $urlParams); + $this->loadBackends(); + $this->loadAuthMechanisms(); + } + + /** + * Load storage backends provided by this app + */ + protected function loadBackends() { $container = $this->getContainer(); + $service = $container->query('OCA\\Files_External\\Service\\BackendService'); - /** - * Controllers - */ - $container->registerService('AjaxController', function (IContainer $c) { - return new AjaxController( - $c->query('AppName'), - $c->query('Request') - ); - }); + $service->registerBackends([ + $container->query('OCA\Files_External\Lib\Backend\Local'), + $container->query('OCA\Files_External\Lib\Backend\AmazonS3'), + $container->query('OCA\Files_External\Lib\Backend\Dropbox'), + $container->query('OCA\Files_External\Lib\Backend\FTP'), + $container->query('OCA\Files_External\Lib\Backend\Google'), + $container->query('OCA\Files_External\Lib\Backend\Swift'), + $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\SFTP_Key'), + ]); + + if (!\OC_Util::runningOnWindows()) { + $service->registerBackends([ + $container->query('OCA\Files_External\Lib\Backend\SMB'), + $container->query('OCA\Files_External\Lib\Backend\SMB_OC'), + ]); + } } + + /** + * Load authentication mechanisms provided by this app + */ + protected function loadAuthMechanisms() { + $container = $this->getContainer(); + $service = $container->query('OCA\\Files_External\\Service\\BackendService'); + + $service->registerAuthMechanisms([ + // AuthMechanism::SCHEME_NULL mechanism + $container->query('OCA\Files_External\Lib\Auth\NullMechanism'), + + // 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/appinfo/routes.php b/apps/files_external/appinfo/routes.php index bc4b0e98c918..514a254af802 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -39,7 +39,7 @@ 'routes' => array( array( 'name' => 'Ajax#getSshKeys', - 'url' => '/ajax/sftp_key.php', + 'url' => '/ajax/public_key.php', 'verb' => 'POST', 'requirements' => array() ) @@ -47,10 +47,10 @@ ) ); -$this->create('files_external_dropbox', 'ajax/dropbox.php') - ->actionInclude('files_external/ajax/dropbox.php'); -$this->create('files_external_google', 'ajax/google.php') - ->actionInclude('files_external/ajax/google.php'); +$this->create('files_external_oauth1', 'ajax/oauth1.php') + ->actionInclude('files_external/ajax/oauth1.php'); +$this->create('files_external_oauth2', 'ajax/oauth2.php') + ->actionInclude('files_external/ajax/oauth2.php'); $this->create('files_external_list_applicable', '/applicable') diff --git a/apps/files_external/controller/ajaxcontroller.php b/apps/files_external/controller/ajaxcontroller.php index cb2de4322868..c285cd34e701 100644 --- a/apps/files_external/controller/ajaxcontroller.php +++ b/apps/files_external/controller/ajaxcontroller.php @@ -25,19 +25,19 @@ use OCP\AppFramework\Controller; use OCP\IRequest; use OCP\AppFramework\Http\JSONResponse; -use phpseclib\Crypt\RSA; +use OCA\Files_External\Lib\Auth\PublicKey\RSA; class AjaxController extends Controller { - public function __construct($appName, IRequest $request) { + /** @var RSA */ + private $rsaMechanism; + + public function __construct($appName, IRequest $request, RSA $rsaMechanism) { parent::__construct($appName, $request); + $this->rsaMechanism = $rsaMechanism; } private function generateSshKeys() { - $rsa = new RSA(); - $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_OPENSSH); - $rsa->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); - - $key = $rsa->createKey(); + $key = $this->rsaMechanism->createKey(); // Replace the placeholder label with a more meaningful one $key['publicKey'] = str_replace('phpseclib-generated-key', gethostname(), $key['publickey']); diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index 33f870d48e32..756a34fc5d40 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -63,7 +63,8 @@ public function __construct( * Create an external storage entry. * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name + * @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 @@ -74,21 +75,27 @@ public function __construct( */ public function create( $mountPoint, - $backendClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, $applicableGroups, $priority ) { - $newStorage = new StorageConfig(); - $newStorage->setMountPoint($mountPoint); - $newStorage->setBackendClass($backendClass); - $newStorage->setBackendOptions($backendOptions); - $newStorage->setMountOptions($mountOptions); - $newStorage->setApplicableUsers($applicableUsers); - $newStorage->setApplicableGroups($applicableGroups); - $newStorage->setPriority($priority); + $newStorage = $this->createStorage( + $mountPoint, + $backend, + $authMechanism, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + if ($newStorage instanceof DataResponse) { + return $newStorage; + } $response = $this->validate($newStorage); if (!empty($response)) { @@ -110,7 +117,8 @@ public function create( * * @param int $id storage id * @param string $mountPoint storage mount point - * @param string $backendClass backend class name + * @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 @@ -122,21 +130,28 @@ public function create( public function update( $id, $mountPoint, - $backendClass, + $backend, + $authMechanism, $backendOptions, $mountOptions, $applicableUsers, $applicableGroups, $priority ) { - $storage = new StorageConfig($id); - $storage->setMountPoint($mountPoint); - $storage->setBackendClass($backendClass); - $storage->setBackendOptions($backendOptions); - $storage->setMountOptions($mountOptions); - $storage->setApplicableUsers($applicableUsers); - $storage->setApplicableGroups($applicableGroups); - $storage->setPriority($priority); + $storage = $this->createStorage( + $mountPoint, + $backend, + $authMechanism, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + if ($storage instanceof DataResponse) { + return $storage; + } + $storage->setId($id); $response = $this->validate($storage); if (!empty($response)) { diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index c09ceacc7d73..3d91af8bd8fb 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -32,6 +32,10 @@ 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 @@ -71,6 +75,51 @@ public function __construct( $this->service = $storagesService; } + /** + * 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) { + return new DataResponse( + [ + 'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class') + ], + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + } + /** * Validate storage config * @@ -89,18 +138,39 @@ protected function validate(StorageConfig $storage) { ); } - // TODO: validate that other attrs are set - - $backends = \OC_Mount_Config::getBackends(); - if (!isset($backends[$storage->getBackendClass()])) { + /** @var Backend */ + $backend = $storage->getBackend(); + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + if (!$backend || $backend->checkDependencies()) { // invalid backend return new DataResponse( array( - 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass())) + 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [ + $storage->getBackend()->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; } @@ -114,14 +184,27 @@ protected function validate(StorageConfig $storage) { * @param StorageConfig $storage storage configuration */ protected function updateStorageStatus(StorageConfig &$storage) { - // update status (can be time-consuming) - $storage->setStatus( - \OC_Mount_Config::getBackendStatus( - $storage->getBackendClass(), - $storage->getBackendOptions(), - false - ) - ); + try { + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + $authMechanism->manipulateStorageConfig($storage); + /** @var Backend */ + $backend = $storage->getBackend(); + $backend->manipulateStorageConfig($storage); + + // update status (can be time-consuming) + $storage->setStatus( + \OC_Mount_Config::getBackendStatus( + $backend->getStorageClass(), + $storage->getBackendOptions(), + false + ) + ); + } catch (InsufficientDataForMeaningfulAnswerException $e) { + $storage->setStatus(\OC_Mount_Config::STATUS_INDETERMINATE); + } catch (StorageNotAvailableException $e) { + $storage->setStatus(\OC_Mount_Config::STATUS_ERROR); + } } /** diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index f5d22e5caa69..0d41e088a761 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -30,8 +30,10 @@ use \OCP\AppFramework\Controller; use \OCP\AppFramework\Http; use \OCA\Files_external\Service\UserStoragesService; +use \OCA\Files_External\Service\BackendService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\Backend\Backend; /** * User storages controller @@ -69,17 +71,20 @@ public function __construct( protected function validate(StorageConfig $storage) { $result = parent::validate($storage); - if ($result != null) { + if ($result !== null) { return $result; } // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage and other disabled backends - $allowedBackends = \OC_Mount_Config::getPersonalBackends(); - if (!isset($allowedBackends[$storage->getBackendClass()])) { + /** @var Backend */ + $backend = $storage->getBackend(); + if (!$backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { return new DataResponse( array( - 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass())) + 'message' => (string)$this->l10n->t('Admin-only storage backend "%s"', [ + $storage->getBackend()->getIdentifier() + ]) ), Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -103,7 +108,8 @@ public function show($id) { * Create an external storage entry. * * @param string $mountPoint storage mount point - * @param string $backendClass backend class name + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -113,15 +119,21 @@ public function show($id) { */ public function create( $mountPoint, - $backendClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ) { - $newStorage = new StorageConfig(); - $newStorage->setMountPoint($mountPoint); - $newStorage->setBackendClass($backendClass); - $newStorage->setBackendOptions($backendOptions); - $newStorage->setMountOptions($mountOptions); + $newStorage = $this->createStorage( + $mountPoint, + $backend, + $authMechanism, + $backendOptions, + $mountOptions + ); + if ($newStorage instanceOf DataResponse) { + return $newStorage; + } $response = $this->validate($newStorage); if (!empty($response)) { @@ -142,7 +154,8 @@ public function create( * * @param int $id storage id * @param string $mountPoint storage mount point - * @param string $backendClass backend class name + * @param string $backend backend identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options * @@ -153,15 +166,22 @@ public function create( public function update( $id, $mountPoint, - $backendClass, + $backend, + $authMechanism, $backendOptions, $mountOptions ) { - $storage = new StorageConfig($id); - $storage->setMountPoint($mountPoint); - $storage->setBackendClass($backendClass); - $storage->setBackendOptions($backendOptions); - $storage->setMountOptions($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)) { diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js deleted file mode 100644 index 53b5d5d666fb..000000000000 --- a/apps/files_external/js/dropbox.js +++ /dev/null @@ -1,111 +0,0 @@ -$(document).ready(function() { - - $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox').each(function() { - var configured = $(this).find('[data-parameter="configured"]'); - if ($(configured).val() == 'true') { - $(this).find('.configuration input').attr('disabled', 'disabled'); - $(this).find('.configuration').append(''+t('files_external', 'Access granted')+''); - } else { - var app_key = $(this).find('.configuration [data-parameter="app_key"]').val(); - var app_secret = $(this).find('.configuration [data-parameter="app_secret"]').val(); - var config = $(this).find('.configuration'); - if (app_key != '' && app_secret != '') { - var pos = window.location.search.indexOf('oauth_token') + 12; - var token = $(this).find('.configuration [data-parameter="token"]'); - if (pos != -1 && window.location.search.substr(pos, $(token).val().length) == $(token).val()) { - var token_secret = $(this).find('.configuration [data-parameter="token_secret"]'); - var tr = $(this); - var statusSpan = $(tr).find('.status span'); - statusSpan.removeClass(); - statusSpan.addClass('waiting'); - $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 2, app_key: app_key, app_secret: app_secret, request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { - if (result && result.status == 'success') { - $(token).val(result.access_token); - $(token_secret).val(result.access_token_secret); - $(configured).val('true'); - OCA.External.Settings.mountConfig.saveStorageConfig(tr, function(status) { - if (status) { - $(tr).find('.configuration input').attr('disabled', 'disabled'); - $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); - } - }); - } else { - OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); - } - }); - } - } else { - onDropboxInputsChange($(this)); - } - } - }); - - $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() { - var tr = $(this).parent(); - setTimeout(function() { - onDropboxInputsChange(tr); - }, 20); - }); - - $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() { - onDropboxInputsChange($(this).parent()); - }); - - $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox .chzn-select', function() { - onDropboxInputsChange($(this).parent().parent()); - }); - - function onDropboxInputsChange(tr) { - if ($(tr).find('[data-parameter="configured"]').val() != 'true') { - var config = $(tr).find('.configuration'); - if ($(tr).find('.mountPoint input').val() != '' - && $(config).find('[data-parameter="app_key"]').val() != '' - && $(config).find('[data-parameter="app_secret"]').val() != '' - && ($(tr).find('.chzn-select').length == 0 - || $(tr).find('.chzn-select').val() != null)) - { - if ($(tr).find('.dropbox').length == 0) { - $(config).append($(document.createElement('input')) - .addClass('button dropbox') - .attr('type', 'button') - .attr('value', t('files_external', 'Grant access'))); - } else { - $(tr).find('.dropbox').show(); - } - } else if ($(tr).find('.dropbox').length > 0) { - $(tr).find('.dropbox').hide(); - } - } - } - - $('#externalStorage').on('click', '.dropbox', function(event) { - event.preventDefault(); - var tr = $(this).parent().parent(); - var app_key = $(this).parent().find('[data-parameter="app_key"]').val(); - var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val(); - if (app_key != '' && app_secret != '') { - var tr = $(this).parent().parent(); - var configured = $(this).parent().find('[data-parameter="configured"]'); - var token = $(this).parent().find('[data-parameter="token"]'); - var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); - $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: location.protocol + '//' + location.host + location.pathname }, function(result) { - if (result && result.status == 'success') { - $(configured).val('false'); - $(token).val(result.data.request_token); - $(token_secret).val(result.data.request_token_secret); - OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() { - window.location = result.data.url; - }); - } else { - OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); - } - }); - } else { - OC.dialogs.alert( - t('files_external', 'Please provide a valid Dropbox app key and secret.'), - t('files_external', 'Error configuring Dropbox storage') - ); - } - }); - -}); diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js deleted file mode 100644 index 648538f80287..000000000000 --- a/apps/files_external/js/google.js +++ /dev/null @@ -1,131 +0,0 @@ -$(document).ready(function() { - - $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function() { - var configured = $(this).find('[data-parameter="configured"]'); - if ($(configured).val() == 'true') { - $(this).find('.configuration input').attr('disabled', 'disabled'); - $(this).find('.configuration').append($('').attr('id', 'access') - .text(t('files_external', 'Access granted'))); - } else { - var client_id = $(this).find('.configuration [data-parameter="client_id"]').val(); - var client_secret = $(this).find('.configuration [data-parameter="client_secret"]') - .val(); - if (client_id != '' && client_secret != '') { - var params = {}; - window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) { - params[key] = value; - }); - if (params['code'] !== undefined) { - var tr = $(this); - var token = $(this).find('.configuration [data-parameter="token"]'); - var statusSpan = $(tr).find('.status span'); - statusSpan.removeClass(); - statusSpan.addClass('waiting'); - $.post(OC.filePath('files_external', 'ajax', 'google.php'), - { - step: 2, - client_id: client_id, - client_secret: client_secret, - redirect: location.protocol + '//' + location.host + location.pathname, - code: params['code'], - }, function(result) { - if (result && result.status == 'success') { - $(token).val(result.data.token); - $(configured).val('true'); - OCA.External.Settings.mountConfig.saveStorageConfig(tr, function(status) { - if (status) { - $(tr).find('.configuration input').attr('disabled', 'disabled'); - $(tr).find('.configuration').append($('') - .attr('id', 'access') - .text(t('files_external', 'Access granted'))); - } - }); - } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Google Drive storage') - ); - } - } - ); - } - } else { - onGoogleInputsChange($(this)); - } - } - }); - - $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', - function() { - var tr = $(this).parent(); - setTimeout(function() { - onGoogleInputsChange(tr); - }, 20); - } - ); - - $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', - function() { - onGoogleInputsChange($(this).parent()); - } - ); - - $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google .chzn-select' - , function() { - onGoogleInputsChange($(this).parent().parent()); - } - ); - - function onGoogleInputsChange(tr) { - if ($(tr).find('[data-parameter="configured"]').val() != 'true') { - var config = $(tr).find('.configuration'); - if ($(tr).find('.mountPoint input').val() != '' - && $(config).find('[data-parameter="client_id"]').val() != '' - && $(config).find('[data-parameter="client_secret"]').val() != '' - && ($(tr).find('.chzn-select').length == 0 - || $(tr).find('.chzn-select').val() != null)) - { - if ($(tr).find('.google').length == 0) { - $(config).append($(document.createElement('input')).addClass('button google') - .attr('type', 'button') - .attr('value', t('files_external', 'Grant access'))); - } else { - $(tr).find('.google').show(); - } - } else if ($(tr).find('.google').length > 0) { - $(tr).find('.google').hide(); - } - } - } - - $('#externalStorage').on('click', '.google', function(event) { - event.preventDefault(); - var tr = $(this).parent().parent(); - var configured = $(this).parent().find('[data-parameter="configured"]'); - var client_id = $(this).parent().find('[data-parameter="client_id"]').val(); - var client_secret = $(this).parent().find('[data-parameter="client_secret"]').val(); - if (client_id != '' && client_secret != '') { - var token = $(this).parent().find('[data-parameter="token"]'); - $.post(OC.filePath('files_external', 'ajax', 'google.php'), - { - step: 1, - client_id: client_id, - client_secret: client_secret, - redirect: location.protocol + '//' + location.host + location.pathname, - }, function(result) { - if (result && result.status == 'success') { - $(configured).val('false'); - $(token).val('false'); - OCA.External.Settings.mountConfig.saveStorageConfig(tr, function(status) { - window.location = result.data.url; - }); - } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Google Drive storage') - ); - } - } - ); - } - }); - -}); diff --git a/apps/files_external/js/oauth1.js b/apps/files_external/js/oauth1.js new file mode 100644 index 000000000000..10c6575c6ddf --- /dev/null +++ b/apps/files_external/js/oauth1.js @@ -0,0 +1,79 @@ +$(document).ready(function() { + + OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme) { + if (authMechanism === 'oauth1::oauth1') { + var config = $tr.find('.configuration'); + config.append($(document.createElement('input')) + .addClass('button auth-param') + .attr('type', 'button') + .attr('value', t('files_external', 'Grant access')) + .attr('name', 'oauth1_grant') + ); + + var configured = $tr.find('[data-parameter="configured"]'); + if ($(configured).val() == 'true') { + $tr.find('.configuration input').attr('disabled', 'disabled'); + $tr.find('.configuration').append(''+t('files_external', 'Access granted')+''); + } else { + var app_key = $tr.find('.configuration [data-parameter="app_key"]').val(); + var app_secret = $tr.find('.configuration [data-parameter="app_secret"]').val(); + if (app_key != '' && app_secret != '') { + var pos = window.location.search.indexOf('oauth_token') + 12; + var token = $tr.find('.configuration [data-parameter="token"]'); + if (pos != -1 && window.location.search.substr(pos, $(token).val().length) == $(token).val()) { + var token_secret = $tr.find('.configuration [data-parameter="token_secret"]'); + var statusSpan = $(tr).find('.status span'); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); + $.post(OC.filePath('files_external', 'ajax', 'oauth1.php'), { step: 2, app_key: app_key, app_secret: app_secret, request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { + if (result && result.status == 'success') { + $(token).val(result.access_token); + $(token_secret).val(result.access_token_secret); + $(configured).val('true'); + OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) { + if (status) { + $tr.find('.configuration input').attr('disabled', 'disabled'); + $tr.find('.configuration').append(''+t('files_external', 'Access granted')+''); + } + }); + } else { + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring OAuth1')); + } + }); + } + } + } + } + }); + + $('#externalStorage').on('click', '[name="oauth1_grant"]', function(event) { + event.preventDefault(); + var tr = $(this).parent().parent(); + var app_key = $(this).parent().find('[data-parameter="app_key"]').val(); + var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val(); + if (app_key != '' && app_secret != '') { + var tr = $(this).parent().parent(); + var configured = $(this).parent().find('[data-parameter="configured"]'); + var token = $(this).parent().find('[data-parameter="token"]'); + var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); + $.post(OC.filePath('files_external', 'ajax', 'oauth1.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: location.protocol + '//' + location.host + location.pathname }, function(result) { + if (result && result.status == 'success') { + $(configured).val('false'); + $(token).val(result.data.request_token); + $(token_secret).val(result.data.request_token_secret); + OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() { + window.location = result.data.url; + }); + } else { + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring OAuth1')); + } + }); + } else { + OC.dialogs.alert( + t('files_external', 'Please provide a valid app key and secret.'), + t('files_external', 'Error configuring OAuth1') + ); + } + }); + +}); diff --git a/apps/files_external/js/oauth2.js b/apps/files_external/js/oauth2.js new file mode 100644 index 000000000000..849414374201 --- /dev/null +++ b/apps/files_external/js/oauth2.js @@ -0,0 +1,95 @@ +$(document).ready(function() { + + OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme) { + if (authMechanism === 'oauth2::oauth2') { + var config = $tr.find('.configuration'); + config.append($(document.createElement('input')) + .addClass('button auth-param') + .attr('type', 'button') + .attr('value', t('files_external', 'Grant access')) + .attr('name', 'oauth2_grant') + ); + + var configured = $tr.find('[data-parameter="configured"]'); + if ($(configured).val() == 'true') { + $tr.find('.configuration input').attr('disabled', 'disabled'); + $tr.find('.configuration').append($('').attr('id', 'access') + .text(t('files_external', 'Access granted'))); + } else { + var client_id = $tr.find('.configuration [data-parameter="client_id"]').val(); + var client_secret = $tr.find('.configuration [data-parameter="client_secret"]') + .val(); + if (client_id != '' && client_secret != '') { + var params = {}; + window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) { + params[key] = value; + }); + if (params['code'] !== undefined) { + var token = $tr.find('.configuration [data-parameter="token"]'); + var statusSpan = $tr.find('.status span'); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); + $.post(OC.filePath('files_external', 'ajax', 'oauth2.php'), + { + step: 2, + client_id: client_id, + client_secret: client_secret, + redirect: location.protocol + '//' + location.host + location.pathname, + code: params['code'], + }, function(result) { + if (result && result.status == 'success') { + $(token).val(result.data.token); + $(configured).val('true'); + OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) { + if (status) { + $tr.find('.configuration input').attr('disabled', 'disabled'); + $tr.find('.configuration').append($('') + .attr('id', 'access') + .text(t('files_external', 'Access granted'))); + } + }); + } else { + OC.dialogs.alert(result.data.message, + t('files_external', 'Error configuring OAuth2') + ); + } + } + ); + } + } + } + } + }); + + $('#externalStorage').on('click', '[name="oauth2_grant"]', function(event) { + event.preventDefault(); + var tr = $(this).parent().parent(); + var configured = $(this).parent().find('[data-parameter="configured"]'); + var client_id = $(this).parent().find('[data-parameter="client_id"]').val(); + var client_secret = $(this).parent().find('[data-parameter="client_secret"]').val(); + if (client_id != '' && client_secret != '') { + var token = $(this).parent().find('[data-parameter="token"]'); + $.post(OC.filePath('files_external', 'ajax', 'oauth2.php'), + { + step: 1, + client_id: client_id, + client_secret: client_secret, + redirect: location.protocol + '//' + location.host + location.pathname, + }, function(result) { + if (result && result.status == 'success') { + $(configured).val('false'); + $(token).val('false'); + OCA.External.Settings.mountConfig.saveStorageConfig(tr, function(status) { + window.location = result.data.url; + }); + } else { + OC.dialogs.alert(result.data.message, + t('files_external', 'Error configuring OAuth2') + ); + } + } + ); + } + }); + +}); diff --git a/apps/files_external/js/public_key.js b/apps/files_external/js/public_key.js new file mode 100644 index 000000000000..a8546067452c --- /dev/null +++ b/apps/files_external/js/public_key.js @@ -0,0 +1,46 @@ +$(document).ready(function() { + + OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme) { + if (scheme === 'publickey') { + var config = $tr.find('.configuration'); + if ($(config).find('[name="public_key_generate"]').length === 0) { + setupTableRow($tr, config); + } + } + }); + + $('#externalStorage').on('click', '[name="public_key_generate"]', function(event) { + event.preventDefault(); + var tr = $(this).parent().parent(); + generateKeys(tr); + }); + + function setupTableRow(tr, config) { + $(config).append($(document.createElement('input')) + .addClass('button auth-param') + .attr('type', 'button') + .attr('value', t('files_external', 'Generate keys')) + .attr('name', 'public_key_generate') + ); + // If there's no private key, build one + if (0 === $(config).find('[data-parameter="private_key"]').val().length) { + generateKeys(tr); + } + } + + function generateKeys(tr) { + var config = $(tr).find('.configuration'); + + $.post(OC.filePath('files_external', 'ajax', 'public_key.php'), {}, function(result) { + if (result && result.status === 'success') { + $(config).find('[data-parameter="public_key"]').val(result.data.public_key); + $(config).find('[data-parameter="private_key"]').val(result.data.private_key); + OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() { + // Nothing to do + }); + } else { + OC.dialogs.alert(result.data.message, t('files_external', 'Error generating key pair') ); + } + }); + } +}); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 287b4664541a..94cb656731ca 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -191,7 +191,8 @@ var StorageConfig = function(id) { StorageConfig.Status = { IN_PROGRESS: -1, SUCCESS: 0, - ERROR: 1 + ERROR: 1, + INDETERMINATE: 2 }; /** * @memberof OCA.External.Settings @@ -214,11 +215,18 @@ StorageConfig.prototype = { mountPoint: '', /** - * Backend class name + * Backend * * @type string */ - backendClass: null, + backend: null, + + /** + * Authentication mechanism + * + * @type string + */ + authMechanism: null, /** * Backend-specific configuration @@ -272,7 +280,8 @@ StorageConfig.prototype = { getData: function() { var data = { mountPoint: this.mountPoint, - backendClass: this.backendClass, + backend: this.backend, + authMechanism: this.authMechanism, backendOptions: this.backendOptions }; if (this.id) { @@ -541,7 +550,7 @@ var MountConfigListView = function($el, options) { /** * @memberOf OCA.External.Settings */ -MountConfigListView.prototype = { +MountConfigListView.prototype = _.extend({ /** * jQuery element containing the config list @@ -579,6 +588,13 @@ MountConfigListView.prototype = { */ _allBackends: null, + /** + * List of all supported authentication mechanisms + * + * @type Object. + */ + _allAuthMechanisms: null, + _encryptionEnabled: false, /** @@ -605,6 +621,7 @@ MountConfigListView.prototype = { // read the backend config that was carefully crammed // into the data-configurations attribute of the select this._allBackends = this.$el.find('.selectBackend').data('configurations'); + this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms'); //initialize hidden input field with list of users and groups this.$el.find('tr:not(#addMountPoint)').each(function(i,tr) { @@ -627,11 +644,31 @@ MountConfigListView.prototype = { addSelect2(this.$el.find('tr:not(#addMountPoint) .applicableUsers'), this._userListLimit); - this.$el.find('tr:not(#addMountPoint)').each(function(i, tr) { + this._initEvents(); + + this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { self.recheckStorageConfig($(tr)); }); + }, - this._initEvents(); + /** + * Custom JS event handlers + * Trigger callback for all existing configurations + */ + whenSelectBackend: function(callback) { + this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { + var backend = $(tr).find('.backend').data('class'); + callback($(tr), backend); + }); + this.on('selectBackend', callback); + }, + whenSelectAuthMechanism: function(callback) { + var self = this; + this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { + var authMechanism = $(tr).find('.selectAuthMechanism').val(); + callback($(tr), authMechanism, self._allAuthMechanisms[authMechanism]['scheme']); + }); + this.on('selectAuthMechanism', callback); }, /** @@ -660,6 +697,7 @@ MountConfigListView.prototype = { }); this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this)); + this.$el.on('change', '.selectAuthMechanism', _.bind(this._onSelectAuthMechanism, this)); }, _onChange: function(event) { @@ -687,47 +725,36 @@ MountConfigListView.prototype = { $el.find('tbody').append($tr.clone()); $el.find('tbody tr').last().find('.mountPoint input').val(''); var selected = $target.find('option:selected').text(); - var backendClass = $target.val(); + var backend = $target.val(); $tr.find('.backend').text(selected); if ($tr.find('.mountPoint input').val() === '') { $tr.find('.mountPoint input').val(this._suggestMountPoint(selected)); } - $tr.addClass(backendClass); - $tr.find('.backend').data('class', backendClass); - var configurations = this._allBackends; - var $td = $tr.find('td.configuration'); - $.each(configurations, function(backend, parameters) { - if (backend === backendClass) { - $.each(parameters['configuration'], function(parameter, placeholder) { - var is_optional = false; - if (placeholder.indexOf('&') === 0) { - is_optional = true; - placeholder = placeholder.substring(1); - } - var newElement; - if (placeholder.indexOf('*') === 0) { - var class_string = is_optional ? ' optional' : ''; - newElement = $(''); - } else if (placeholder.indexOf('!') === 0) { - newElement = $(''); - } else if (placeholder.indexOf('#') === 0) { - newElement = $(''); - } else { - var class_string = is_optional ? ' optional' : ''; - newElement = $(''); - } - highlightInput(newElement); - $td.append(newElement); - }); - var priorityEl = $(''); - $tr.append(priorityEl); - if (parameters['custom'] && $el.find('tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length === 1) { - OC.addScript('files_external', parameters['custom']); - } - $td.children().not('[type=hidden]').first().focus(); - return false; + $tr.addClass(backend); + $tr.find('.backend').data('class', backend); + var backendConfiguration = this._allBackends[backend]; + + var selectAuthMechanism = $(''); + $.each(this._allAuthMechanisms, function(authClass, authMechanism) { + if (backendConfiguration['authSchemes'][authMechanism['scheme']]) { + selectAuthMechanism.append( + $('') + ); } }); + $tr.find('td.authentication').append(selectAuthMechanism); + + var $td = $tr.find('td.configuration'); + $.each(backendConfiguration['configuration'], _.partial(this.writeParameterInput, $td)); + + this.trigger('selectBackend', $tr, backend); + + selectAuthMechanism.trigger('change'); // generate configuration parameters for auth mechanism + + var priorityEl = $(''); + $tr.append(priorityEl); + $td.children().not('[type=hidden]').first().focus(); + $tr.find('td').last().attr('class', 'remove'); $tr.find('td.mountOptionsToggle').removeClass('hidden'); $tr.find('td').last().removeAttr('style'); @@ -736,6 +763,45 @@ MountConfigListView.prototype = { addSelect2($tr.find('.applicableUsers'), this._userListLimit); }, + _onSelectAuthMechanism: function(event) { + var $target = $(event.target); + var $tr = $target.closest('tr'); + + var authMechanism = $target.val(); + var authMechanismConfiguration = this._allAuthMechanisms[authMechanism]; + var $td = $tr.find('td.configuration'); + $td.find('.auth-param').remove(); + + $.each(authMechanismConfiguration['configuration'], _.partial( + this.writeParameterInput, $td, _, _, ['auth-param'] + )); + + this.trigger('selectAuthMechanism', + $tr, authMechanism, authMechanismConfiguration['scheme'] + ); + }, + + writeParameterInput: function($td, parameter, placeholder, classes) { + classes = $.isArray(classes) ? classes : []; + classes.push('added'); + if (placeholder.indexOf('&') === 0) { + classes.push('optional'); + placeholder = placeholder.substring(1); + } + var newElement; + if (placeholder.indexOf('*') === 0) { + newElement = $(''); + } else if (placeholder.indexOf('!') === 0) { + newElement = $(''); + } else if (placeholder.indexOf('#') === 0) { + newElement = $(''); + } else { + newElement = $(''); + } + highlightInput(newElement); + $td.append(newElement); + }, + /** * Gets the storage model from the given row * @@ -750,7 +816,8 @@ MountConfigListView.prototype = { } var storage = new this._storageConfigClass(storageId); storage.mountPoint = $tr.find('.mountPoint input').val(); - storage.backendClass = $tr.find('.backend').data('class'); + storage.backend = $tr.find('.backend').data('class'); + storage.authMechanism = $tr.find('.selectAuthMechanism').val(); var classOptions = {}; var configuration = $tr.find('.configuration input'); @@ -903,7 +970,7 @@ MountConfigListView.prototype = { */ updateStatus: function($tr, status) { var $statusSpan = $tr.find('.status span'); - $statusSpan.removeClass('success error loading-small'); + $statusSpan.removeClass('loading-small success indeterminate error'); switch (status) { case StorageConfig.Status.IN_PROGRESS: $statusSpan.addClass('loading-small'); @@ -911,6 +978,9 @@ MountConfigListView.prototype = { case StorageConfig.Status.SUCCESS: $statusSpan.addClass('success'); break; + case StorageConfig.Status.INDETERMINATE: + $statusSpan.addClass('indeterminate'); + break; default: $statusSpan.addClass('error'); } @@ -992,7 +1062,7 @@ MountConfigListView.prototype = { self.saveStorageConfig($tr); }); } -}; +}, OC.Backbone.Events); $(document).ready(function() { var enabled = $('#files_external').attr('data-encryption-enabled'); diff --git a/apps/files_external/js/sftp_key.js b/apps/files_external/js/sftp_key.js deleted file mode 100644 index 55b11b1fac99..000000000000 --- a/apps/files_external/js/sftp_key.js +++ /dev/null @@ -1,53 +0,0 @@ -$(document).ready(function() { - - $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\SFTP_Key').each(function() { - var tr = $(this); - var config = $(tr).find('.configuration'); - if ($(config).find('.sftp_key').length === 0) { - setupTableRow(tr, config); - } - }); - - // We can't catch the DOM elements being added, but we can pick up when - // they receive focus - $('#externalStorage').on('focus', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\SFTP_Key', function() { - var tr = $(this); - var config = $(tr).find('.configuration'); - - if ($(config).find('.sftp_key').length === 0) { - setupTableRow(tr, config); - } - }); - - $('#externalStorage').on('click', '.sftp_key', function(event) { - event.preventDefault(); - var tr = $(this).parent().parent(); - generateKeys(tr); - }); - - function setupTableRow(tr, config) { - $(config).append($(document.createElement('input')).addClass('button sftp_key') - .attr('type', 'button') - .attr('value', t('files_external', 'Generate keys'))); - // If there's no private key, build one - if (0 === $(config).find('[data-parameter="private_key"]').val().length) { - generateKeys(tr); - } - } - - function generateKeys(tr) { - var config = $(tr).find('.configuration'); - - $.post(OC.filePath('files_external', 'ajax', 'sftp_key.php'), {}, function(result) { - if (result && result.status === 'success') { - $(config).find('[data-parameter="public_key"]').val(result.data.public_key); - $(config).find('[data-parameter="private_key"]').val(result.data.private_key); - OCA.External.mountConfig.saveStorageConfig(tr, function() { - // Nothing to do - }); - } else { - OC.dialogs.alert(result.data.message, t('files_external', 'Error generating key pair') ); - } - }); - } -}); diff --git a/apps/files_external/lib/auth/amazons3/accesskey.php b/apps/files_external/lib/auth/amazons3/accesskey.php new file mode 100644 index 000000000000..9e3aab374b90 --- /dev/null +++ b/apps/files_external/lib/auth/amazons3/accesskey.php @@ -0,0 +1,47 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\AmazonS3; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * Amazon S3 access key authentication + */ +class AccessKey extends AuthMechanism { + + const SCHEME_AMAZONS3_ACCESSKEY = 'amazons3_accesskey'; + + public function __construct(IL10N $l) { + $this + ->setIdentifier('amazons3::accesskey') + ->setScheme(self::SCHEME_AMAZONS3_ACCESSKEY) + ->setText($l->t('Access key')) + ->addParameters([ + (new DefinitionParameter('key', $l->t('Access key'))), + (new DefinitionParameter('secret', $l->t('Secret key'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + ]); + } + +} diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php new file mode 100644 index 000000000000..dacb40a77bb7 --- /dev/null +++ b/apps/files_external/lib/auth/authmechanism.php @@ -0,0 +1,99 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\IdentifierTrait; +use \OCA\Files_External\Lib\FrontendDefinitionTrait; +use \OCA\Files_External\Lib\StorageModifierTrait; + +/** + * Authentication mechanism + */ +class AuthMechanism implements \JsonSerializable { + + /** Standard authentication schemes */ + const SCHEME_NULL = 'null'; + const SCHEME_BUILTIN = 'builtin'; + const SCHEME_PASSWORD = 'password'; + const SCHEME_OAUTH1 = 'oauth1'; + const SCHEME_OAUTH2 = 'oauth2'; + const SCHEME_PUBLICKEY = 'publickey'; + const SCHEME_OPENSTACK = 'openstack'; + + use VisibilityTrait; + use FrontendDefinitionTrait; + use StorageModifierTrait; + use IdentifierTrait; + + /** @var string */ + protected $scheme; + + /** + * Get the authentication scheme implemented + * See self::SCHEME_* constants + * + * @return string + */ + public function getScheme() { + return $this->scheme; + } + + /** + * @param string $scheme + * @return self + */ + public function setScheme($scheme) { + $this->scheme = $scheme; + return $this; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerialize() { + $data = $this->jsonSerializeDefinition(); + $data['scheme'] = $this->getScheme(); + + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorage(StorageConfig $storage) { + // does the backend actually support this scheme + $supportedSchemes = $storage->getBackend()->getAuthSchemes(); + if (!isset($supportedSchemes[$this->getScheme()])) { + return false; + } + + return $this->validateStorageDefinition($storage); + } + +} diff --git a/apps/files_external/lib/auth/builtin.php b/apps/files_external/lib/auth/builtin.php new file mode 100644 index 000000000000..ee28a4e8a5cb --- /dev/null +++ b/apps/files_external/lib/auth/builtin.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_external\Lib\StorageConfig; + +/** + * Builtin authentication mechanism, for legacy backends + */ +class Builtin extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('builtin::builtin') + ->setScheme(self::SCHEME_BUILTIN) + ->setText($l->t('Builtin')) + ; + } + +} diff --git a/apps/files_external/lib/auth/nullmechanism.php b/apps/files_external/lib/auth/nullmechanism.php new file mode 100644 index 000000000000..1765fc673968 --- /dev/null +++ b/apps/files_external/lib/auth/nullmechanism.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_external\Lib\StorageConfig; + +/** + * Null authentication mechanism + */ +class NullMechanism extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('null::null') + ->setScheme(self::SCHEME_NULL) + ->setText($l->t('None')) + ; + } + +} diff --git a/apps/files_external/lib/auth/oauth1/oauth1.php b/apps/files_external/lib/auth/oauth1/oauth1.php new file mode 100644 index 000000000000..3fb1b16aa6d2 --- /dev/null +++ b/apps/files_external/lib/auth/oauth1/oauth1.php @@ -0,0 +1,53 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\OAuth1; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * OAuth1 authentication + */ +class OAuth1 extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('oauth1::oauth1') + ->setScheme(self::SCHEME_OAUTH1) + ->setText($l->t('OAuth1')) + ->addParameters([ + (new DefinitionParameter('configured', 'configured')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + (new DefinitionParameter('app_key', $l->t('App key'))), + (new DefinitionParameter('app_secret', $l->t('App secret'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + (new DefinitionParameter('token', 'token')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + (new DefinitionParameter('token_secret', 'token_secret')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + ]) + ->setCustomJs('oauth1') + ; + } + +} diff --git a/apps/files_external/lib/auth/oauth2/oauth2.php b/apps/files_external/lib/auth/oauth2/oauth2.php new file mode 100644 index 000000000000..73faa85a44e9 --- /dev/null +++ b/apps/files_external/lib/auth/oauth2/oauth2.php @@ -0,0 +1,51 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\OAuth2; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * OAuth2 authentication + */ +class OAuth2 extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('oauth2::oauth2') + ->setScheme(self::SCHEME_OAUTH2) + ->setText($l->t('OAuth2')) + ->addParameters([ + (new DefinitionParameter('configured', 'configured')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + (new DefinitionParameter('client_id', $l->t('Client ID'))), + (new DefinitionParameter('client_secret', $l->t('Client secret'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + (new DefinitionParameter('token', 'token')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + ]) + ->setCustomJs('oauth2') + ; + } + +} diff --git a/apps/files_external/lib/auth/openstack/openstack.php b/apps/files_external/lib/auth/openstack/openstack.php new file mode 100644 index 000000000000..faf356bcf2ed --- /dev/null +++ b/apps/files_external/lib/auth/openstack/openstack.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\OpenStack; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * OpenStack Keystone authentication + */ +class OpenStack extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('openstack::openstack') + ->setScheme(self::SCHEME_OPENSTACK) + ->setText($l->t('OpenStack')) + ->addParameters([ + (new DefinitionParameter('user', $l->t('Username'))), + (new DefinitionParameter('password', $l->t('Password'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + (new DefinitionParameter('tenant', $l->t('Tenant name'))), + (new DefinitionParameter('url', $l->t('Identity endpoint URL'))), + ]) + ; + } + +} diff --git a/apps/files_external/lib/auth/openstack/rackspace.php b/apps/files_external/lib/auth/openstack/rackspace.php new file mode 100644 index 000000000000..9268f3aad87e --- /dev/null +++ b/apps/files_external/lib/auth/openstack/rackspace.php @@ -0,0 +1,46 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\OpenStack; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * Rackspace authentication + */ +class Rackspace extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('openstack::rackspace') + ->setScheme(self::SCHEME_OPENSTACK) + ->setText($l->t('Rackspace')) + ->addParameters([ + (new DefinitionParameter('user', $l->t('Username'))), + (new DefinitionParameter('key', $l->t('API key'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + ]) + ; + } + +} diff --git a/apps/files_external/lib/auth/password/password.php b/apps/files_external/lib/auth/password/password.php new file mode 100644 index 000000000000..96ad4b496d4f --- /dev/null +++ b/apps/files_external/lib/auth/password/password.php @@ -0,0 +1,45 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\Password; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * Basic password authentication mechanism + */ +class Password extends AuthMechanism { + + public function __construct(IL10N $l) { + $this + ->setIdentifier('password::password') + ->setScheme(self::SCHEME_PASSWORD) + ->setText($l->t('Username and password')) + ->addParameters([ + (new DefinitionParameter('user', $l->t('Username'))), + (new DefinitionParameter('password', $l->t('Password'))) + ->setType(DefinitionParameter::VALUE_PASSWORD), + ]); + } + +} diff --git a/apps/files_external/lib/auth/password/sessioncredentials.php b/apps/files_external/lib/auth/password/sessioncredentials.php new file mode 100644 index 000000000000..37cfd97a1768 --- /dev/null +++ b/apps/files_external/lib/auth/password/sessioncredentials.php @@ -0,0 +1,84 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\Password; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Lib\StorageConfig; +use \OCP\ISession; +use \OCP\Security\ICrypto; +use \OCP\Files\Storage; +use \OCA\Files_External\Lib\SessionStorageWrapper; +use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; + +/** + * Username and password from login credentials, saved in session + */ +class SessionCredentials extends AuthMechanism { + + /** @var ISession */ + protected $session; + + /** @var ICrypto */ + protected $crypto; + + public function __construct(IL10N $l, ISession $session, ICrypto $crypto) { + $this->session = $session; + $this->crypto = $crypto; + + $this + ->setIdentifier('password::sessioncredentials') + ->setScheme(self::SCHEME_PASSWORD) + ->setText($l->t('Session credentials')) + ->addParameters([ + ]) + ; + + \OCP\Util::connectHook('OC_User', 'post_login', $this, 'authenticate'); + } + + /** + * Hook listener on post login + * + * @param array $params + */ + public function authenticate(array $params) { + $this->session->set('password::sessioncredentials/credentials', $this->crypto->encrypt(json_encode($params))); + } + + public function manipulateStorageConfig(StorageConfig &$storage) { + $encrypted = $this->session->get('password::sessioncredentials/credentials'); + if (!isset($encrypted)) { + throw new InsufficientDataForMeaningfulAnswerException('No session credentials saved'); + } + + $credentials = json_decode($this->crypto->decrypt($encrypted), true); + $storage->setBackendOption('user', $this->session->get('loginname')); + $storage->setBackendOption('password', $credentials['password']); + } + + public function wrapStorage(Storage $storage) { + return new SessionStorageWrapper(['storage' => $storage]); + } + +} diff --git a/apps/files_external/lib/auth/publickey/rsa.php b/apps/files_external/lib/auth/publickey/rsa.php new file mode 100644 index 000000000000..bcf87e1f02f0 --- /dev/null +++ b/apps/files_external/lib/auth/publickey/rsa.php @@ -0,0 +1,80 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Auth\PublicKey; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Lib\StorageConfig; +use \OCP\IConfig; +use \phpseclib\Crypt\RSA as RSACrypt; + +/** + * RSA public key authentication + */ +class RSA extends AuthMechanism { + + const CREATE_KEY_BITS = 2048; + + /** @var IConfig */ + private $config; + + public function __construct(IL10N $l, IConfig $config) { + $this->config = $config; + + $this + ->setIdentifier('publickey::rsa') + ->setScheme(self::SCHEME_PUBLICKEY) + ->setText($l->t('RSA public key')) + ->addParameters([ + (new DefinitionParameter('user', $l->t('Username'))), + (new DefinitionParameter('public_key', $l->t('Public key'))), + (new DefinitionParameter('private_key', 'private_key')) + ->setType(DefinitionParameter::VALUE_HIDDEN), + ]) + ->setCustomJs('public_key') + ; + } + + public function manipulateStorageConfig(StorageConfig &$storage) { + $auth = new RSACrypt(); + $auth->setPassword($this->config->getSystemValue('secret', '')); + if (!$auth->loadKey($storage->getBackendOption('private_key'))) { + throw new \RuntimeException('unable to load private key'); + } + $storage->setBackendOption('public_key_auth', $auth); + } + + /** + * Generate a keypair + * + * @return array ['privatekey' => $privateKey, 'publickey' => $publicKey] + */ + public function createKey() { + $rsa = new RSACrypt(); + $rsa->setPublicKeyFormat(RSACrypt::PUBLIC_FORMAT_OPENSSH); + $rsa->setPassword($this->config->getSystemValue('secret', '')); + + return $rsa->createKey(self::CREATE_KEY_BITS); + } + +} diff --git a/apps/files_external/lib/backend/amazons3.php b/apps/files_external/lib/backend/amazons3.php new file mode 100644 index 000000000000..880d47621f3b --- /dev/null +++ b/apps/files_external/lib/backend/amazons3.php @@ -0,0 +1,58 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\AmazonS3\AccessKey; + +class AmazonS3 extends Backend { + + public function __construct(IL10N $l, AccessKey $legacyAuth) { + $this + ->setIdentifier('amazons3') + ->addIdentifierAlias('\OC\Files\Storage\AmazonS3') // legacy compat + ->setStorageClass('\OC\Files\Storage\AmazonS3') + ->setText($l->t('Amazon S3')) + ->addParameters([ + (new DefinitionParameter('bucket', $l->t('Bucket'))), + (new DefinitionParameter('hostname', $l->t('Hostname'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('port', $l->t('Port'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('region', $l->t('Region'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('use_ssl', $l->t('Enable SSL'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + (new DefinitionParameter('use_path_style', $l->t('Enable Path Style'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + ]) + ->setDependencyCheck('\OC\Files\Storage\AmazonS3::checkDependencies') + ->addAuthScheme(AccessKey::SCHEME_AMAZONS3_ACCESSKEY) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php new file mode 100644 index 000000000000..9ec44092e95c --- /dev/null +++ b/apps/files_external/lib/backend/backend.php @@ -0,0 +1,143 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\FrontendDefinitionTrait; +use \OCA\Files_External\Lib\PriorityTrait; +use \OCA\Files_External\Lib\DependencyTrait; +use \OCA\Files_External\Lib\StorageModifierTrait; +use \OCA\Files_External\Lib\IdentifierTrait; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * Storage backend + */ +class Backend implements \JsonSerializable { + + use VisibilityTrait; + use FrontendDefinitionTrait; + use PriorityTrait; + use DependencyTrait; + use StorageModifierTrait; + use IdentifierTrait; + + /** @var string storage class */ + private $storageClass; + + /** @var array 'scheme' => true, supported authentication schemes */ + private $authSchemes = []; + + /** @var AuthMechanism|callable authentication mechanism fallback */ + private $legacyAuthMechanism; + + /** + * @return string + */ + public function getStorageClass() { + return $this->storageClass; + } + + /** + * @param string $class + * @return self + */ + public function setStorageClass($class) { + $this->storageClass = $class; + return $this; + } + + /** + * @return array + */ + public function getAuthSchemes() { + if (empty($this->authSchemes)) { + return [AuthMechanism::SCHEME_NULL => true]; + } + return $this->authSchemes; + } + + /** + * @param string $scheme + * @return self + */ + public function addAuthScheme($scheme) { + $this->authSchemes[$scheme] = true; + return $this; + } + + /** + * @param array $parameters storage parameters, for dynamic mechanism selection + * @return AuthMechanism + */ + public function getLegacyAuthMechanism(array $parameters = []) { + if (is_callable($this->legacyAuthMechanism)) { + return call_user_func($this->legacyAuthMechanism, $parameters); + } + return $this->legacyAuthMechanism; + } + + /** + * @param AuthMechanism $authMechanism + * @return self + */ + public function setLegacyAuthMechanism(AuthMechanism $authMechanism) { + $this->legacyAuthMechanism = $authMechanism; + return $this; + } + + /** + * @param callable $callback dynamic auth mechanism selection + * @return self + */ + public function setLegacyAuthMechanismCallback(callable $callback) { + $this->legacyAuthMechanism = $callback; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerialize() { + $data = $this->jsonSerializeDefinition(); + + $data['backend'] = $data['name']; // legacy compat + $data['priority'] = $this->getPriority(); + $data['authSchemes'] = $this->getAuthSchemes(); + + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorage(StorageConfig $storage) { + return $this->validateStorageDefinition($storage); + } + +} + diff --git a/apps/files_external/lib/backend/dav.php b/apps/files_external/lib/backend/dav.php new file mode 100644 index 000000000000..5ae6d1225882 --- /dev/null +++ b/apps/files_external/lib/backend/dav.php @@ -0,0 +1,53 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; + +use \OCA\Files_External\Lib\Auth\Password\Password; + +class DAV extends Backend { + + public function __construct(IL10N $l, Password $legacyAuth) { + $this + ->setIdentifier('dav') + ->addIdentifierAlias('\OC\Files\Storage\DAV') // legacy compat + ->setStorageClass('\OC\Files\Storage\DAV') + ->setText($l->t('WebDAV')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('URL'))), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('secure', $l->t('Secure https://'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + ]) + ->setDependencyCheck('\OC\Files\Storage\DAV::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/dropbox.php b/apps/files_external/lib/backend/dropbox.php new file mode 100644 index 000000000000..bfd2e4cddc49 --- /dev/null +++ b/apps/files_external/lib/backend/dropbox.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\OAuth1\OAuth1; + +class Dropbox extends Backend { + + public function __construct(IL10N $l, OAuth1 $legacyAuth) { + $this + ->setIdentifier('dropbox') + ->addIdentifierAlias('\OC\Files\Storage\Dropbox') // legacy compat + ->setStorageClass('\OC\Files\Storage\Dropbox') + ->setText($l->t('Dropbox')) + ->addParameters([ + // all parameters handled in OAuth1 mechanism + ]) + ->setDependencyCheck('\OC\Files\Storage\Dropbox::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_OAUTH1) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/ftp.php b/apps/files_external/lib/backend/ftp.php new file mode 100644 index 000000000000..df6ca37679ec --- /dev/null +++ b/apps/files_external/lib/backend/ftp.php @@ -0,0 +1,53 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; + +use \OCA\Files_External\Lib\Auth\Password\Password; + +class FTP extends Backend { + + public function __construct(IL10N $l, Password $legacyAuth) { + $this + ->setIdentifier('ftp') + ->addIdentifierAlias('\OC\Files\Storage\FTP') // legacy compat + ->setStorageClass('\OC\Files\Storage\FTP') + ->setText($l->t('FTP')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('Host'))), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('secure', $l->t('Secure ftps://'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + ]) + ->setDependencyCheck('\OC\Files\Storage\FTP::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/google.php b/apps/files_external/lib/backend/google.php new file mode 100644 index 000000000000..b46b2f653a64 --- /dev/null +++ b/apps/files_external/lib/backend/google.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\OAuth2\OAuth2; + +class Google extends Backend { + + public function __construct(IL10N $l, OAuth2 $legacyAuth) { + $this + ->setIdentifier('googledrive') + ->addIdentifierAlias('\OC\Files\Storage\Google') // legacy compat + ->setStorageClass('\OC\Files\Storage\Google') + ->setText($l->t('Google Drive')) + ->addParameters([ + // all parameters handled in OAuth2 mechanism + ]) + ->setDependencyCheck('\OC\Files\Storage\Google::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_OAUTH2) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/legacybackend.php b/apps/files_external/lib/backend/legacybackend.php new file mode 100644 index 000000000000..0f60c2caa478 --- /dev/null +++ b/apps/files_external/lib/backend/legacybackend.php @@ -0,0 +1,85 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\Builtin; + +/** + * Legacy compatibility for OC_Mount_Config::registerBackend() + */ +class LegacyBackend extends Backend { + + /** + * @param string $class + * @param array $definition + * @param Builtin $authMechanism + */ + public function __construct($class, array $definition, Builtin $authMechanism) { + $this + ->setIdentifier($class) + ->setStorageClass($class) + ->setText($definition['backend']) + ->addAuthScheme(Builtin::SCHEME_BUILTIN) + ->setLegacyAuthMechanism($authMechanism) + ; + + foreach ($definition['configuration'] as $name => $placeholder) { + $flags = DefinitionParameter::FLAG_NONE; + $type = DefinitionParameter::VALUE_TEXT; + if ($placeholder[0] === '&') { + $flags = DefinitionParameter::FLAG_OPTIONAL; + $placeholder = substr($placeholder, 1); + } + switch ($placeholder[0]) { + case '!': + $type = DefinitionParameter::VALUE_BOOLEAN; + $placeholder = substr($placeholder, 1); + break; + case '*': + $type = DefinitionParameter::VALUE_PASSWORD; + $placeholder = substr($placeholder, 1); + break; + case '#': + $type = DefinitionParameter::VALUE_HIDDEN; + $placeholder = substr($placeholder, 1); + break; + } + $this->addParameter((new DefinitionParameter($name, $placeholder)) + ->setType($type) + ->setFlags($flags) + ); + } + + if (isset($definition['priority'])) { + $this->setPriority($definition['priority']); + } + if (isset($definition['custom'])) { + $this->setCustomJs($definition['custom']); + } + if (isset($definition['has_dependencies']) && $definition['has_dependencies']) { + $this->setDependencyCheck($class . '::checkDependencies'); + } + } + +} diff --git a/apps/files_external/lib/backend/local.php b/apps/files_external/lib/backend/local.php new file mode 100644 index 000000000000..a80b437fab78 --- /dev/null +++ b/apps/files_external/lib/backend/local.php @@ -0,0 +1,49 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\NullMechanism; + +class Local extends Backend { + + public function __construct(IL10N $l, NullMechanism $legacyAuth) { + $this + ->setIdentifier('local') + ->addIdentifierAlias('\OC\Files\Storage\Local') // legacy compat + ->setStorageClass('\OC\Files\Storage\Local') + ->setText($l->t('Local')) + ->addParameters([ + (new DefinitionParameter('datadir', $l->t('Location'))), + ]) + ->setAllowedVisibility(BackendService::VISIBILITY_ADMIN) + ->setPriority(BackendService::PRIORITY_DEFAULT + 50) + ->addAuthScheme(AuthMechanism::SCHEME_NULL) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/owncloud.php b/apps/files_external/lib/backend/owncloud.php new file mode 100644 index 000000000000..d06625de2419 --- /dev/null +++ b/apps/files_external/lib/backend/owncloud.php @@ -0,0 +1,52 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; + +use \OCA\Files_External\Lib\Auth\Password\Password; + +class OwnCloud extends Backend { + + public function __construct(IL10N $l, Password $legacyAuth) { + $this + ->setIdentifier('owncloud') + ->addIdentifierAlias('\OC\Files\Storage\OwnCloud') // legacy compat + ->setStorageClass('\OC\Files\Storage\OwnCloud') + ->setText($l->t('ownCloud')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('URL'))), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('secure', $l->t('Secure https://'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + ]) + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/sftp.php b/apps/files_external/lib/backend/sftp.php new file mode 100644 index 000000000000..c0bcd27c54b4 --- /dev/null +++ b/apps/files_external/lib/backend/sftp.php @@ -0,0 +1,51 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; + +use \OCA\Files_External\Lib\Auth\Password\Password; + +class SFTP extends Backend { + + public function __construct(IL10N $l, Password $legacyAuth) { + $this + ->setIdentifier('sftp') + ->addIdentifierAlias('\OC\Files\Storage\SFTP') // legacy compat + ->setStorageClass('\OC\Files\Storage\SFTP') + ->setText($l->t('SFTP')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('Host'))), + (new DefinitionParameter('root', $l->t('Root'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + ]) + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->addAuthScheme(AuthMechanism::SCHEME_PUBLICKEY) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/sftp_key.php b/apps/files_external/lib/backend/sftp_key.php new file mode 100644 index 000000000000..4a7f565eb199 --- /dev/null +++ b/apps/files_external/lib/backend/sftp_key.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\PublicKey\RSA; + +class SFTP_Key extends Backend { + + public function __construct(IL10N $l, RSA $legacyAuth) { + $this + ->setIdentifier('\OC\Files\Storage\SFTP_Key') + ->setStorageClass('\OC\Files\Storage\SFTP') + ->setText($l->t('SFTP with secret key login [DEPRECATED]')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('Host'))), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + ]) + ->addAuthScheme(AuthMechanism::SCHEME_PUBLICKEY) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/smb.php b/apps/files_external/lib/backend/smb.php new file mode 100644 index 000000000000..3d950a80c314 --- /dev/null +++ b/apps/files_external/lib/backend/smb.php @@ -0,0 +1,52 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; + +use \OCA\Files_External\Lib\Auth\Password\Password; + +class SMB extends Backend { + + public function __construct(IL10N $l, Password $legacyAuth) { + $this + ->setIdentifier('smb') + ->addIdentifierAlias('\OC\Files\Storage\SMB') // legacy compat + ->setStorageClass('\OC\Files\Storage\SMB') + ->setText($l->t('SMB / CIFS')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('Host'))), + (new DefinitionParameter('share', $l->t('Share'))), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + ]) + ->setDependencyCheck('\OC\Files\Storage\SMB::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + +} diff --git a/apps/files_external/lib/backend/smb_oc.php b/apps/files_external/lib/backend/smb_oc.php new file mode 100644 index 000000000000..cae3818c76c6 --- /dev/null +++ b/apps/files_external/lib/backend/smb_oc.php @@ -0,0 +1,67 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\Password\SessionCredentials; +use \OCA\Files_External\Lib\StorageConfig; + +/** + * Deprecated SMB_OC class - use SMB with the password::sessioncredentials auth mechanism + */ +class SMB_OC extends Backend { + + public function __construct(IL10N $l, SessionCredentials $legacyAuth) { + $this + ->setIdentifier('\OC\Files\Storage\SMB_OC') + ->setStorageClass('\OC\Files\Storage\SMB') + ->setText($l->t('SMB / CIFS using OC login [DEPRECATED]')) + ->addParameters([ + (new DefinitionParameter('host', $l->t('Host'))), + (new DefinitionParameter('username_as_share', $l->t('Username as share'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN), + (new DefinitionParameter('share', $l->t('Share'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('root', $l->t('Remote subfolder'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + ]) + ->setDependencyCheck('\OC\Files\Storage\SMB::checkDependencies') + ->setPriority(BackendService::PRIORITY_DEFAULT - 10) + ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) + ->setLegacyAuthMechanism($legacyAuth) + ; + } + + public function manipulateStorageConfig(StorageConfig &$storage) { + $username_as_share = ($storage->getBackendOption('username_as_share') === 'true'); + + if ($this->username_as_share) { + $share = '/' . $storage->getBackendOption('user'); + $storage->setBackendOption('share', $share); + } + } + +} diff --git a/apps/files_external/lib/backend/swift.php b/apps/files_external/lib/backend/swift.php new file mode 100644 index 000000000000..9f01a923638b --- /dev/null +++ b/apps/files_external/lib/backend/swift.php @@ -0,0 +1,60 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib\Backend; + +use \OCP\IL10N; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\Auth\AuthMechanism; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Auth\OpenStack\OpenStack; +use \OCA\Files_External\Lib\Auth\OpenStack\Rackspace; + +class Swift extends Backend { + + public function __construct(IL10N $l, OpenStack $openstackAuth, Rackspace $rackspaceAuth) { + $this + ->setIdentifier('swift') + ->addIdentifierAlias('\OC\Files\Storage\Swift') // legacy compat + ->setStorageClass('\OC\Files\Storage\Swift') + ->setText($l->t('OpenStack Object Storage')) + ->addParameters([ + (new DefinitionParameter('service_name', $l->t('Service name'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('region', $l->t('Region'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('bucket', $l->t('Bucket'))), + (new DefinitionParameter('timeout', $l->t('Request timeout (seconds)'))) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + ]) + ->setDependencyCheck('\OC\Files\Storage\Swift::checkDependencies') + ->addAuthScheme(AuthMechanism::SCHEME_OPENSTACK) + ->setLegacyAuthMechanismCallback(function(array $params) use ($openstackAuth, $rackspaceAuth) { + if (isset($params['key'])) { + return $rackspaceAuth; + } + return $openstackAuth; + }) + ; + } + +} diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 8fcf39cc7677..e51526c39923 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -32,6 +32,10 @@ */ use phpseclib\Crypt\AES; +use \OCP\AppFramework\IAppContainer; +use \OCA\Files_External\Lib\BackendConfig; +use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Backend\LegacyBackend; /** * Class to configure mount.json globally and for users @@ -47,75 +51,39 @@ class OC_Mount_Config { // getBackendStatus return types const STATUS_SUCCESS = 0; const STATUS_ERROR = 1; + const STATUS_INDETERMINATE = 2; // whether to skip backend test (for unit tests, as this static class is not mockable) public static $skipTest = false; - private static $backends = array(); + /** @var IAppContainer */ + public static $appContainer; /** - * @param string $class - * @param array $definition - * @return bool - */ - public static function registerBackend($class, $definition) { - if (!isset($definition['backend'])) { - return false; - } - - OC_Mount_Config::$backends[$class] = $definition; - return true; - } - - /** - * Setup backends + * Teach OC_Mount_Config about the AppFramework * - * @return array of previously registered backends + * @param IAppContainer $appContainer */ - public static function setUp($backends = array()) { - $backup = self::$backends; - self::$backends = $backends; - - return $backup; + public static function initApp(IAppContainer $appContainer) { + self::$appContainer = $appContainer; } /** - * Get details on each of the external storage backends, used for the mount config UI - * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded - * If the configuration parameter should be secret, add a '*' to the beginning of the value - * If the configuration parameter is a boolean, add a '!' to the beginning of the value - * If the configuration parameter is optional, add a '&' to the beginning of the value - * If the configuration parameter is hidden, add a '#' to the beginning of the value - * - * @return array + * @param string $class + * @param array $definition + * @return bool + * @deprecated 8.2.0 use \OCA\Files_External\Service\BackendService::registerBackend() */ - public static function getBackends() { - $sortFunc = function ($a, $b) { - return strcasecmp($a['backend'], $b['backend']); - }; - - $backEnds = array(); - - foreach (OC_Mount_Config::$backends as $class => $backend) { - if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) { - if (!method_exists($class, 'checkDependencies')) { - \OCP\Util::writeLog('files_external', - "Backend class $class has dependencies but doesn't provide method checkDependencies()", - \OCP\Util::DEBUG); - continue; - } elseif ($class::checkDependencies() !== true) { - continue; - } - } - $backEnds[$class] = $backend; - } + public static function registerBackend($class, $definition) { + $backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService'); + $auth = self::$appContainer->query('OCA\Files_External\Lib\Auth\Builtin'); - uasort($backEnds, $sortFunc); + $backendService->registerBackend(new LegacyBackend($class, $definition, $auth)); - return $backEnds; + return true; } - /** + /* * Hook that mounts the given user's visible mount points * * @param array $data @@ -152,138 +120,63 @@ public static function initMountPointsHook($data) { * Returns the mount points for the given user. * The mount point is relative to the data directory. * - * @param string $user user + * @param string $uid user * @return array of mount point string as key, mountpoint config as value */ - public static function getAbsoluteMountPoints($user) { + public static function getAbsoluteMountPoints($uid) { $mountPoints = array(); - $backends = self::getBackends(); - - // Load system mount points - $mountConfig = self::readData(); + $userGlobalStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserGlobalStoragesService'); + $userStoragesService = self::$appContainer->query('OCA\Files_External\Service\UserStoragesService'); + $user = self::$appContainer->query('OCP\IUserManager')->get($uid); - // Global mount points (is this redundant?) - if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) { - foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) { - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; - } + $userGlobalStoragesService->setUser($user); + $userStoragesService->setUser($user); - // Override if priority greater - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backends[$options['class']]['backend']; - $mountPoints[$mountPoint] = $options; - } + foreach ($userGlobalStoragesService->getAllStorages() as $storage) { + $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); + $mountPoints[$mountPoint] = self::prepareMountPointEntry($storage, false); + foreach ($mountEntry['options'] as &$option) { + $option = self::setUserVars($uid, $option); } + $mountPoints[$mountPoint] = $mountEntry; } - // All user mount points - if (isset($mountConfig[self::MOUNT_TYPE_USER]) && isset($mountConfig[self::MOUNT_TYPE_USER]['all'])) { - $mounts = $mountConfig[self::MOUNT_TYPE_USER]['all']; - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; - } - // Override if priority greater - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; - $options['backend'] = $backends[$options['class']]['backend']; - $mountPoints[$mountPoint] = $options; - } - } - } - // Group mount points - if (isset($mountConfig[self::MOUNT_TYPE_GROUP])) { - foreach ($mountConfig[self::MOUNT_TYPE_GROUP] as $group => $mounts) { - if (\OC_Group::inGroup($user, $group)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; - } - - // Override if priority greater or if priority type different - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_GROUP) - ) { - $options['priority_type'] = self::MOUNT_TYPE_GROUP; - $options['backend'] = $backends[$options['class']]['backend']; - $mountPoints[$mountPoint] = $options; - } - } - } - } - } - // User mount points - if (isset($mountConfig[self::MOUNT_TYPE_USER])) { - foreach ($mountConfig[self::MOUNT_TYPE_USER] as $mountUser => $mounts) { - if (strtolower($mountUser) === strtolower($user)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - $options['personal'] = false; - $options['options'] = self::decryptPasswords($options['options']); - if (!isset($options['priority'])) { - $options['priority'] = $backends[$options['class']]['priority']; - } - - // Override if priority greater or if priority type different - if ((!isset($mountPoints[$mountPoint])) - || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) - || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_USER) - ) { - $options['priority_type'] = self::MOUNT_TYPE_USER; - $options['backend'] = $backends[$options['class']]['backend']; - $mountPoints[$mountPoint] = $options; - } - } - } + foreach ($userStoragesService->getAllStorages() as $storage) { + $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); + $mountEntry = self::prepareMountPointEntry($storage, true); + foreach ($mountEntry['options'] as &$option) { + $option = self::setUserVars($uid, $option); } + $mountPoints[$mountPoint] = $mountEntry; } - $personalBackends = self::getPersonalBackends(); - - // Load personal mount points - $mountConfig = self::readData($user); - if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) { - foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) { - if (isset($personalBackends[$options['class']])) { - $options['personal'] = true; - $options['options'] = self::decryptPasswords($options['options']); - - // Always override previous config - $options['priority_type'] = self::MOUNT_TYPE_PERSONAL; - $options['backend'] = $backends[$options['class']]['backend']; - $mountPoints[$mountPoint] = $options; - } - } - } + $userGlobalStoragesService->resetUser(); + $userStoragesService->resetUser(); return $mountPoints; } + /** + * Convert a StorageConfig to the legacy mountPoints array format + * + * @param StorageConfig $storage + * @param bool $isPersonal + * @return array + */ + private static function prepareMountPointEntry(StorageConfig $storage, $isPersonal) { + $mountEntry = []; + $mountEntry['class'] = $mountEntry['backend'] = $storage->getBackend()->getIdentifier(); + $mountEntry['authMechanism'] = $storage->getAuthMechanism()->getIdentifier(); + $mountEntry['personal'] = $isPersonal; + $mountEntry['options'] = self::decryptPasswords($storage->getBackendOptions()); + $priority = $storage->getPriority(); + if (isset($priority)) { + $mountEntry['priority'] = $priority; + } + return $mountEntry; + } + /** * fill in the correct values for $user * @@ -291,7 +184,7 @@ public static function getAbsoluteMountPoints($user) { * @param string|array $input * @return string */ - private static function setUserVars($user, $input) { + public static function setUserVars($user, $input) { if (is_array($input)) { foreach ($input as &$value) { if (is_string($value)) { @@ -304,181 +197,6 @@ private static function setUserVars($user, $input) { return $input; } - - /** - * Get details on each of the external storage backends, used for the mount config UI - * Some backends are not available as a personal backend, f.e. Local and such that have - * been disabled by the admin. - * - * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded - * If the configuration parameter should be secret, add a '*' to the beginning of the value - * If the configuration parameter is a boolean, add a '!' to the beginning of the value - * If the configuration parameter is optional, add a '&' to the beginning of the value - * If the configuration parameter is hidden, add a '#' to the beginning of the value - * - * @return array - */ - public static function getPersonalBackends() { - - // Check whether the user has permissions to add personal storage backends - // return an empty array if this is not the case - if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') { - return array(); - } - - $backEnds = self::getBackends(); - - // Remove local storage and other disabled storages - unset($backEnds['\OC\Files\Storage\Local']); - - $allowedBackEnds = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); - foreach ($backEnds as $backend => $null) { - if (!in_array($backend, $allowedBackEnds)) { - unset($backEnds[$backend]); - } - } - - return $backEnds; - } - - /** - * Get the system mount points - * The returned array is not in the same format as getUserMountPoints() - * - * @return array - */ - public static function getSystemMountPoints() { - $mountPoints = self::readData(); - $backends = self::getBackends(); - $system = array(); - if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) { - foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) { - foreach ($mounts as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $mount['options'] = self::decryptPasswords($mount['options']); - if (!isset($mount['priority'])) { - $mount['priority'] = $backends[$mount['class']]['priority']; - } - // Remove '/$user/files/' from mount point - $mountPoint = substr($mountPoint, 13); - - $config = array( - 'class' => $mount['class'], - 'mountpoint' => $mountPoint, - 'backend' => $backends[$mount['class']]['backend'], - 'priority' => $mount['priority'], - 'options' => $mount['options'], - 'applicable' => array('groups' => array($group), 'users' => array()) - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $hash = self::makeConfigHash($config); - // If an existing config exists (with same class, mountpoint and options) - if (isset($system[$hash])) { - // add the groups into that config - $system[$hash]['applicable']['groups'] - = array_merge($system[$hash]['applicable']['groups'], array($group)); - } else { - $system[$hash] = $config; - } - } - } - } - if (isset($mountPoints[self::MOUNT_TYPE_USER])) { - foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) { - foreach ($mounts as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $mount['options'] = self::decryptPasswords($mount['options']); - if (!isset($mount['priority'])) { - $mount['priority'] = $backends[$mount['class']]['priority']; - } - // Remove '/$user/files/' from mount point - $mountPoint = substr($mountPoint, 13); - $config = array( - 'class' => $mount['class'], - 'mountpoint' => $mountPoint, - 'backend' => $backends[$mount['class']]['backend'], - 'priority' => $mount['priority'], - 'options' => $mount['options'], - 'applicable' => array('groups' => array(), 'users' => array($user)) - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $hash = self::makeConfigHash($config); - // If an existing config exists (with same class, mountpoint and options) - if (isset($system[$hash])) { - // add the users into that config - $system[$hash]['applicable']['users'] - = array_merge($system[$hash]['applicable']['users'], array($user)); - } else { - $system[$hash] = $config; - } - } - } - } - return array_values($system); - } - - /** - * Get the personal mount points of the current user - * The returned array is not in the same format as getUserMountPoints() - * - * @return array - */ - public static function getPersonalMountPoints() { - $mountPoints = self::readData(OCP\User::getUser()); - $backEnds = self::getBackends(); - $uid = OCP\User::getUser(); - $personal = array(); - if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) { - foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) { - // Update old classes to new namespace - if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15); - } - $mount['options'] = self::decryptPasswords($mount['options']); - $config = array( - 'class' => $mount['class'], - // Remove '/uid/files/' from mount point - 'mountpoint' => substr($mountPoint, strlen($uid) + 8), - 'backend' => $backEnds[$mount['class']]['backend'], - 'options' => $mount['options'] - ); - if (isset($mount['id'])) { - $config['id'] = (int)$mount['id']; - } - if (isset($mount['storage_id'])) { - $config['storage_id'] = (int)$mount['storage_id']; - } - if (isset($mount['mountOptions'])) { - $config['mountOptions'] = $mount['mountOptions']; - } - $personal[] = $config; - } - } - return $personal; - } - /** * Test connecting using the given backend configuration * @@ -514,163 +232,6 @@ public static function getBackendStatus($class, $options, $isPersonal) { return self::STATUS_ERROR; } - /** - * Add a mount point to the filesystem - * - * @param string $mountPoint Mount point - * @param string $class Backend class - * @param array $classOptions Backend parameters for the class - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @param string $applicable User or group to apply mount to - * @param bool $isPersonal Personal or system mount point i.e. is this being called from the personal or admin page - * @param int|null $priority Mount point priority, null for default - * @return boolean - * - * @deprecated use StoragesService#addStorage() instead - */ - public static function addMountPoint($mountPoint, - $class, - $classOptions, - $mountType, - $applicable, - $isPersonal = false, - $priority = null) { - $backends = self::getBackends(); - $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); - $relMountPoint = $mountPoint; - if ($mountPoint === '' || $mountPoint === '/') { - // can't mount at root folder - return false; - } - - if (!isset($backends[$class])) { - // invalid backend - return false; - } - if ($isPersonal) { - // Verify that the mount point applies for the current user - // Prevent non-admin users from mounting local storage and other disabled backends - $allowed_backends = self::getPersonalBackends(); - if ($applicable != OCP\User::getUser() || !isset($allowed_backends[$class])) { - return false; - } - $mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/'); - } else { - $mountPoint = '/$user/files/' . ltrim($mountPoint, '/'); - } - - $mount = array($applicable => array( - $mountPoint => array( - 'class' => $class, - 'options' => self::encryptPasswords($classOptions)) - ) - ); - if (!$isPersonal && !is_null($priority)) { - $mount[$applicable][$mountPoint]['priority'] = $priority; - } - - $mountPoints = self::readData($isPersonal ? OCP\User::getUser() : null); - // who else loves multi-dimensional array ? - $isNew = !isset($mountPoints[$mountType]) || - !isset($mountPoints[$mountType][$applicable]) || - !isset($mountPoints[$mountType][$applicable][$mountPoint]); - $mountPoints = self::mergeMountPoints($mountPoints, $mount, $mountType); - - // Set default priority if none set - if (!isset($mountPoints[$mountType][$applicable][$mountPoint]['priority'])) { - if (isset($backends[$class]['priority'])) { - $mountPoints[$mountType][$applicable][$mountPoint]['priority'] - = $backends[$class]['priority']; - } else { - $mountPoints[$mountType][$applicable][$mountPoint]['priority'] - = 100; - } - } - - self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints); - - $result = self::getBackendStatus($class, $classOptions, $isPersonal); - if ($result === self::STATUS_SUCCESS && $isNew) { - \OC_Hook::emit( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_create_mount, - array( - \OC\Files\Filesystem::signal_param_path => $relMountPoint, - \OC\Files\Filesystem::signal_param_mount_type => $mountType, - \OC\Files\Filesystem::signal_param_users => $applicable, - ) - ); - } - return $result; - } - - /** - * - * @param string $mountPoint Mount point - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @param string $applicable User or group to remove mount from - * @param bool $isPersonal Personal or system mount point - * @return bool - * - * @deprecated use StoragesService#removeStorage() instead - */ - public static function removeMountPoint($mountPoint, $mountType, $applicable, $isPersonal = false) { - // Verify that the mount point applies for the current user - $relMountPoints = $mountPoint; - if ($isPersonal) { - if ($applicable != OCP\User::getUser()) { - return false; - } - $mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/'); - } else { - $mountPoint = '/$user/files/' . ltrim($mountPoint, '/'); - } - $mountPoint = \OC\Files\Filesystem::normalizePath($mountPoint); - $mountPoints = self::readData($isPersonal ? OCP\User::getUser() : null); - // Remove mount point - unset($mountPoints[$mountType][$applicable][$mountPoint]); - // Unset parent arrays if empty - if (empty($mountPoints[$mountType][$applicable])) { - unset($mountPoints[$mountType][$applicable]); - if (empty($mountPoints[$mountType])) { - unset($mountPoints[$mountType]); - } - } - self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints); - \OC_Hook::emit( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_delete_mount, - array( - \OC\Files\Filesystem::signal_param_path => $relMountPoints, - \OC\Files\Filesystem::signal_param_mount_type => $mountType, - \OC\Files\Filesystem::signal_param_users => $applicable, - ) - ); - return true; - } - - /** - * - * @param string $mountPoint Mount point - * @param string $target The new mount point - * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER - * @return bool - */ - public static function movePersonalMountPoint($mountPoint, $target, $mountType) { - $mountPoint = rtrim($mountPoint, '/'); - $user = OCP\User::getUser(); - $mountPoints = self::readData($user); - if (!isset($mountPoints[$mountType][$user][$mountPoint])) { - return false; - } - $mountPoints[$mountType][$user][$target] = $mountPoints[$mountType][$user][$mountPoint]; - // Remove old mount point - unset($mountPoints[$mountType][$user][$mountPoint]); - - self::writeData($user, $mountPoints); - return true; - } - /** * Read the mount points in the config file into an array * @@ -721,74 +282,35 @@ public static function writeData($user, $data) { } /** - * check dependencies + * Get backend dependency message + * TODO: move into AppFramework along with templates + * + * @param BackendConfig[] $backends + * @return string */ - public static function checkDependencies() { - $dependencies = array(); - foreach (OC_Mount_Config::$backends as $class => $backend) { - if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) { - $result = $class::checkDependencies(); - if ($result !== true) { - if (!is_array($result)) { - $result = array($result); - } - foreach ($result as $key => $value) { - if (is_numeric($key)) { - OC_Mount_Config::addDependency($dependencies, $value, $backend['backend']); - } else { - OC_Mount_Config::addDependency($dependencies, $key, $backend['backend'], $value); - } - } - } - } - } - - if (count($dependencies) > 0) { - return OC_Mount_Config::generateDependencyMessage($dependencies); - } - return ''; - } - - private static function addDependency(&$dependencies, $module, $backend, $message = null) { - if (!isset($dependencies[$module])) { - $dependencies[$module] = array(); - } - - if ($message === null) { - $dependencies[$module][] = $backend; - } else { - $dependencies[$module][] = array('backend' => $backend, 'message' => $message); - } - } - - private static function generateDependencyMessage($dependencies) { + public static function dependencyMessage($backends) { $l = new \OC_L10N('files_external'); - $dependencyMessage = ''; - foreach ($dependencies as $module => $backends) { - $dependencyGroup = array(); - foreach ($backends as $backend) { - if (is_array($backend)) { - $dependencyMessage .= '
' . $l->t('Note: ') . $backend['message']; + $message = ''; + $dependencyGroups = []; + + foreach ($backends as $backend) { + foreach ($backend->checkDependencies() as $dependency) { + if ($message = $dependency->getMessage()) { + $message .= '
' . $l->t('Note: ') . $message; } else { - $dependencyGroup[] = $backend; + $dependencyGroups[$dependency->getDependency()][] = $backend; } } + } - $dependencyGroupCount = count($dependencyGroup); - if ($dependencyGroupCount > 0) { - $backends = ''; - for ($i = 0; $i < $dependencyGroupCount; $i++) { - if ($i > 0 && $i === $dependencyGroupCount - 1) { - $backends .= ' ' . $l->t('and') . ' '; - } elseif ($i > 0) { - $backends .= ', '; - } - $backends .= '' . $dependencyGroup[$i] . ''; - } - $dependencyMessage .= '
' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends); - } + foreach ($dependencyGroups as $module => $dependants) { + $backends = implode(', ', array_map(function($backend) { + return '' . $backend->getText() . ''; + }, $dependants)); + $message .= '
' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends); } - return $dependencyMessage; + + return $message; } /** @@ -918,7 +440,8 @@ private static function getCipher() { public static function makeConfigHash($config) { $data = json_encode( array( - 'c' => $config['class'], + 'c' => $config['backend'], + 'a' => $config['authMechanism'], 'm' => $config['mountpoint'], 'o' => $config['options'], 'p' => isset($config['priority']) ? $config['priority'] : -1, @@ -967,7 +490,8 @@ private static function addStorageId(&$options) { return false; } - $class = $options['class']; + $service = self::$appContainer->query('OCA\Files_External\Service\BackendService'); + $class = $service->getBackend($options['backend'])->getStorageClass(); try { /** @var \OC\Files\Storage\Storage $storage */ $storage = new $class($options['options']); diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index b5c2ba4fc920..b51e07e5ee86 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -2,6 +2,7 @@ /** * @author Morris Jobke * @author Robin Appelman + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -22,16 +23,81 @@ namespace OCA\Files_External\Config; +use OCP\Files\Storage; use OC\Files\Mount\MountPoint; use OCP\Files\Storage\IStorageFactory; -use OCA\Files_External\PersonalMount; +use OCA\Files_External\Lib\PersonalMount; use OCP\Files\Config\IMountProvider; use OCP\IUser; +use OCA\Files_external\Service\UserStoragesService; +use OCA\Files_External\Service\UserGlobalStoragesService; +use OCA\Files_External\Lib\StorageConfig; +use OCP\Files\StorageNotAvailableException; +use OCA\Files_External\Lib\FailedStorage; /** * Make the old files_external config work with the new public mount config api */ class ConfigAdapter implements IMountProvider { + + /** @var UserStoragesService */ + private $userStoragesService; + + /** @var UserGlobalStoragesService */ + private $userGlobalStoragesService; + + /** + * @param UserStoragesService $userStoragesService + * @param UserGlobalStoragesService $userGlobalStoragesService + */ + public function __construct( + UserStoragesService $userStoragesService, + UserGlobalStoragesService $userGlobalStoragesService + ) { + $this->userStoragesService = $userStoragesService; + $this->userGlobalStoragesService = $userGlobalStoragesService; + } + + /** + * Process storage ready for mounting + * + * @param StorageConfig $storage + * @param IUser $user + */ + private function prepareStorageConfig(StorageConfig &$storage, IUser $user) { + foreach ($storage->getBackendOptions() as $option => $value) { + $storage->setBackendOption($option, \OC_Mount_Config::setUserVars( + $user->getUID(), $value + )); + } + + $objectStore = $storage->getBackendOption('objectstore'); + if ($objectStore) { + $objectClass = $objectStore['class']; + $storage->setBackendOption('objectstore', new $objectClass($objectStore)); + } + + $storage->getAuthMechanism()->manipulateStorageConfig($storage); + $storage->getBackend()->manipulateStorageConfig($storage); + } + + /** + * Construct the storage implementation + * + * @param StorageConfig $storageConfig + * @return Storage + */ + private function constructStorage(StorageConfig $storageConfig) { + $class = $storageConfig->getBackend()->getStorageClass(); + $storage = new $class($storageConfig->getBackendOptions()); + + // auth mechanism should fire first + $storage = $storageConfig->getBackend()->wrapStorage($storage); + $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage); + + return $storage; + } + /** * Get all mountpoints applicable for the user * @@ -40,20 +106,53 @@ class ConfigAdapter implements IMountProvider { * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $loader) { - $mountPoints = \OC_Mount_Config::getAbsoluteMountPoints($user->getUID()); - $mounts = array(); - foreach ($mountPoints as $mountPoint => $options) { - if (isset($options['options']['objectstore'])) { - $objectClass = $options['options']['objectstore']['class']; - $options['options']['objectstore'] = new $objectClass($options['options']['objectstore']); + $mounts = []; + + $this->userStoragesService->setUser($user); + $this->userGlobalStoragesService->setUser($user); + + foreach ($this->userGlobalStoragesService->getAllStorages() as $storage) { + try { + $this->prepareStorageConfig($storage, $user); + $impl = $this->constructStorage($storage); + } catch (\Exception $e) { + // propagate exception into filesystem + $impl = new FailedStorage(['exception' => $e]); } - $mountOptions = isset($options['mountOptions']) ? $options['mountOptions'] : []; - if (isset($options['personal']) && $options['personal']) { - $mounts[] = new PersonalMount($options['class'], $mountPoint, $options['options'], $loader, $mountOptions); - } else { - $mounts[] = new MountPoint($options['class'], $mountPoint, $options['options'], $loader, $mountOptions); + + $mount = new MountPoint( + $impl, + '/'.$user->getUID().'/files' . $storage->getMountPoint(), + null, + $loader, + $storage->getMountOptions() + ); + $mounts[$storage->getMountPoint()] = $mount; + } + + foreach ($this->userStoragesService->getAllStorages() as $storage) { + try { + $this->prepareStorageConfig($storage, $user); + $impl = $this->constructStorage($storage); + } catch (\Exception $e) { + // propagate exception into filesystem + $impl = new FailedStorage(['exception' => $e]); } + + $mount = new PersonalMount( + $impl, + '/'.$user->getUID().'/files' . $storage->getMountPoint(), + null, + $loader, + $storage->getMountOptions() + ); + $mount->attachStoragesService($this->userStoragesService, $storage->getId()); + $mounts[$storage->getMountPoint()] = $mount; } + + $this->userStoragesService->resetUser(); + $this->userGlobalStoragesService->resetUser(); + return $mounts; } } diff --git a/apps/files_external/lib/definitionparameter.php b/apps/files_external/lib/definitionparameter.php new file mode 100644 index 000000000000..4b560908b697 --- /dev/null +++ b/apps/files_external/lib/definitionparameter.php @@ -0,0 +1,179 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * Parameter for an external storage definition + */ +class DefinitionParameter implements \JsonSerializable { + + /** Value constants */ + const VALUE_TEXT = 0; + const VALUE_BOOLEAN = 1; + const VALUE_PASSWORD = 2; + const VALUE_HIDDEN = 3; + + /** Flag constants */ + const FLAG_NONE = 0; + const FLAG_OPTIONAL = 1; + + /** @var string name of parameter */ + private $name; + + /** @var string human-readable parameter text */ + private $text; + + /** @var int value type, see self::VALUE_* constants */ + private $type = self::VALUE_TEXT; + + /** @var int flags, see self::FLAG_* constants */ + private $flags = self::FLAG_NONE; + + /** + * @param string $name + * @param string $text + */ + public function __construct($name, $text) { + $this->name = $name; + $this->text = $text; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return string + */ + public function getText() { + return $this->text; + } + + /** + * Get value type + * + * @return int + */ + public function getType() { + return $this->type; + } + + /** + * Set value type + * + * @param int $type + * @return self + */ + public function setType($type) { + $this->type = $type; + return $this; + } + + /** + * @return int + */ + public function getFlags() { + return $this->flags; + } + + /** + * @param int $flags + * @return self + */ + public function setFlags($flags) { + $this->flags = $flags; + return $this; + } + + /** + * @param int $flag + * @return self + */ + public function setFlag($flag) { + $this->flags |= $flag; + return $this; + } + + /** + * @param int $flag + * @return bool + */ + public function isFlagSet($flag) { + return (bool) $this->flags & $flag; + } + + /** + * Serialize into JSON for client-side JS + * + * @return string + */ + public function jsonSerialize() { + $prefix = ''; + switch ($this->getType()) { + case self::VALUE_BOOLEAN: + $prefix = '!'; + break; + case self::VALUE_PASSWORD: + $prefix = '*'; + break; + case self::VALUE_HIDDEN: + $prefix = '#'; + break; + } + + switch ($this->getFlags()) { + case self::FLAG_OPTIONAL: + $prefix = '&' . $prefix; + break; + } + + return $prefix . $this->getText(); + } + + /** + * Validate a parameter value against this + * + * @param mixed $value Value to check + * @return bool success + */ + public function validateValue($value) { + if ($this->getFlags() & self::FLAG_OPTIONAL) { + return true; + } + switch ($this->getType()) { + case self::VALUE_BOOLEAN: + if (!is_bool($value)) { + return false; + } + break; + default: + if (empty($value)) { + return false; + } + break; + } + return true; + } +} diff --git a/apps/files_external/lib/dependencytrait.php b/apps/files_external/lib/dependencytrait.php new file mode 100644 index 000000000000..116421eab144 --- /dev/null +++ b/apps/files_external/lib/dependencytrait.php @@ -0,0 +1,86 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Lib\MissingDependency; + +/** + * Trait for objects that have dependencies for use + */ +trait DependencyTrait { + + /** @var callable|null dependency check */ + private $dependencyCheck = null; + + /** + * @return bool + */ + public function hasDependencies() { + return !is_null($this->dependencyCheck); + } + + /** + * @param callable $dependencyCheck + * @return self + */ + public function setDependencyCheck(callable $dependencyCheck) { + $this->dependencyCheck = $dependencyCheck; + return $this; + } + + /** + * Check if object is valid for use + * + * @return MissingDependency[] Unsatisfied dependencies + */ + public function checkDependencies() { + $ret = []; + + if ($this->hasDependencies()) { + $result = call_user_func($this->dependencyCheck); + if ($result !== true) { + if (!is_array($result)) { + $result = [$result]; + } + foreach ($result as $key => $value) { + if (!($value instanceof MissingDependency)) { + $module = null; + $message = null; + if (is_numeric($key)) { + $module = $value; + } else { + $module = $key; + $message = $value; + } + $value = new MissingDependency($module, $this); + $value->setMessage($message); + } + $ret[] = $value; + } + } + } + + return $ret; + } + +} + diff --git a/apps/files_external/lib/failedstorage.php b/apps/files_external/lib/failedstorage.php new file mode 100644 index 000000000000..6afa98052c2e --- /dev/null +++ b/apps/files_external/lib/failedstorage.php @@ -0,0 +1,200 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Lock\ILockingProvider; +use \OC\Files\Storage\Common; +use \OCP\Files\StorageNotAvailableException; + +/** + * Storage placeholder to represent a missing precondition, storage unavailable + */ +class FailedStorage extends Common { + + /** @var \Exception */ + protected $e; + + /** + * @param array $params ['exception' => \Exception] + */ + public function __construct($params) { + $this->e = $params['exception']; + } + + public function getId() { + // we can't return anything sane here + return 'failedstorage'; + } + + public function mkdir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function rmdir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function opendir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function is_dir($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function is_file($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function stat($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filetype($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filesize($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isCreatable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isReadable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isUpdatable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isDeletable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function isSharable($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getPermissions($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_exists($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function filemtime($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_get_contents($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function file_put_contents($path, $data) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function unlink($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function rename($path1, $path2) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function copy($path1, $path2) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function fopen($path, $mode) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getMimeType($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function hash($type, $path, $raw = false) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function free_space($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function search($query) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function touch($path, $mtime = null) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getLocalFile($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getLocalFolder($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function hasUpdated($path, $time) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getETag($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function getDirectDownload($path) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function verifyPath($path, $fileName) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function acquireLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function releaseLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + + public function changeLock($path, $type, ILockingProvider $provider) { + throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); + } + +} diff --git a/apps/files_external/lib/frontenddefinitiontrait.php b/apps/files_external/lib/frontenddefinitiontrait.php new file mode 100644 index 000000000000..4b826372d2f9 --- /dev/null +++ b/apps/files_external/lib/frontenddefinitiontrait.php @@ -0,0 +1,147 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Lib\DefinitionParameter; +use \OCA\Files_External\Lib\StorageConfig; + +/** + * Trait for objects that have a frontend representation + */ +trait FrontendDefinitionTrait { + + /** @var string human-readable mechanism name */ + private $text; + + /** @var DefinitionParameter[] parameters for mechanism */ + private $parameters = []; + + /** @var string|null custom JS */ + private $customJs = null; + + /** + * @return string + */ + public function getText() { + return $this->text; + } + + /** + * @param string $text + * @return self + */ + public function setText($text) { + $this->text = $text; + return $this; + } + + /** + * @param FrontendDefinitionTrait $a + * @param FrontendDefinitionTrait $b + * @return int + */ + public static function lexicalCompare(FrontendDefinitionTrait $a, FrontendDefinitionTrait $b) { + return strcmp($a->getText(), $b->getText()); + } + + /** + * @return DefinitionParameter[] + */ + public function getParameters() { + return $this->parameters; + } + + /** + * @param DefinitionParameter[] $parameters + * @return self + */ + public function addParameters(array $parameters) { + foreach ($parameters as $parameter) { + $this->addParameter($parameter); + } + return $this; + } + + /** + * @param DefinitionParameter $parameter + * @return self + */ + public function addParameter(DefinitionParameter $parameter) { + $this->parameters[$parameter->getName()] = $parameter; + return $this; + } + + /** + * @return string|null + */ + public function getCustomJs() { + return $this->customJs; + } + + /** + * @param string $custom + * @return self + */ + public function setCustomJs($custom) { + $this->customJs = $custom; + return $this; + } + + /** + * Serialize into JSON for client-side JS + * + * @return array + */ + public function jsonSerializeDefinition() { + $configuration = []; + foreach ($this->getParameters() as $parameter) { + $configuration[$parameter->getName()] = $parameter; + } + + $data = [ + 'name' => $this->getText(), + 'configuration' => $configuration, + ]; + if (isset($this->customJs)) { + $data['custom'] = $this->getCustomJs(); + } + return $data; + } + + /** + * Check if parameters are satisfied in a StorageConfig + * + * @param StorageConfig $storage + * @return bool + */ + public function validateStorageDefinition(StorageConfig $storage) { + $options = $storage->getBackendOptions(); + foreach ($this->getParameters() as $name => $parameter) { + $value = isset($options[$name]) ? $options[$name] : null; + if (!$parameter->validateValue($value)) { + return false; + } + } + return true; + } + +} diff --git a/apps/files_external/lib/identifiertrait.php b/apps/files_external/lib/identifiertrait.php new file mode 100644 index 000000000000..139911580fcc --- /dev/null +++ b/apps/files_external/lib/identifiertrait.php @@ -0,0 +1,68 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * Trait for objects requiring an identifier (and/or identifier aliases) + */ +trait IdentifierTrait { + + /** @var string */ + protected $identifier; + + /** @var string[] */ + protected $identifierAliases = []; + + /** + * @return string + */ + public function getIdentifier() { + return $this->identifier; + } + + /** + * @param string $identifier + * @return self + */ + public function setIdentifier($identifier) { + $this->identifier = $identifier; + $this->identifierAliases[] = $identifier; + return $this; + } + + /** + * @return string[] + */ + public function getIdentifierAliases() { + return $this->identifierAliases; + } + + /** + * @param string $alias + * @return self + */ + public function addIdentifierAlias($alias) { + $this->identifierAliases[] = $alias; + return $this; + } + +} diff --git a/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php new file mode 100644 index 000000000000..dd4cd75df12d --- /dev/null +++ b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php @@ -0,0 +1,30 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Files\StorageNotAvailableException; + +/** + * Authentication mechanism or backend has insufficient data + */ +class InsufficientDataForMeaningfulAnswerException extends StorageNotAvailableException { +} diff --git a/apps/files_external/lib/missingdependency.php b/apps/files_external/lib/missingdependency.php new file mode 100644 index 000000000000..9b25aeacc9b4 --- /dev/null +++ b/apps/files_external/lib/missingdependency.php @@ -0,0 +1,64 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +/** + * External storage backend dependency + */ +class MissingDependency { + + /** @var string */ + private $dependency; + + /** @var string|null Custom message */ + private $message = null; + + /** + * @param string $dependency + */ + public function __construct($dependency) { + $this->dependency = $dependency; + } + + /** + * @return string + */ + public function getDependency() { + return $this->dependency; + } + + /** + * @return string|null + */ + public function getMessage() { + return $this->message; + } + + /** + * @param string $message + * @return self + */ + public function setMessage($message) { + $this->message = $message; + return $this; + } +} diff --git a/apps/files_external/lib/personalmount.php b/apps/files_external/lib/personalmount.php index bbffc9586416..d7f46430e15a 100644 --- a/apps/files_external/lib/personalmount.php +++ b/apps/files_external/lib/personalmount.php @@ -20,15 +20,33 @@ * */ -namespace OCA\Files_External; +namespace OCA\Files_External\Lib; use OC\Files\Mount\MountPoint; use OC\Files\Mount\MoveableMount; +use OCA\Files_External\Service\UserStoragesService; /** * Person mount points can be moved by the user */ class PersonalMount extends MountPoint implements MoveableMount { + /** @var UserStoragesService */ + protected $storagesService; + + /** @var int */ + protected $storageId; + + /** + * Attach a UserStoragesService for mount actions + * + * @param UserStoragesService $storagesService + * @param int $storageId + */ + public function attachStoragesService(UserStoragesService $storagesService, $storageId) { + $this->storagesService = $storagesService; + $this->storageId = $storageId; + } + /** * Move the mount point to $target * @@ -36,9 +54,13 @@ class PersonalMount extends MountPoint implements MoveableMount { * @return bool */ public function moveMount($target) { - $result = \OC_Mount_Config::movePersonalMountPoint($this->getMountPoint(), $target, \OC_Mount_Config::MOUNT_TYPE_USER); + $storage = $this->storagesService->getStorage($this->storageId); + // remove "/$user/files" prefix + $targetParts = explode('/', trim($target, '/'), 3); + $storage->setMountPoint($targetParts[2]); + $this->storagesService->updateStorage($storage); $this->setMountPoint($target); - return $result; + return true; } /** @@ -47,8 +69,7 @@ public function moveMount($target) { * @return bool */ public function removeMount() { - $user = \OCP\User::getUser(); - $relativeMountPoint = substr($this->getMountPoint(), strlen('/' . $user . '/files/')); - return \OC_Mount_Config::removeMountPoint($relativeMountPoint, \OC_Mount_Config::MOUNT_TYPE_USER, $user , true); + $this->storagesService->removeStorage($this->storageId); + return true; } } diff --git a/apps/files_external/lib/prioritytrait.php b/apps/files_external/lib/prioritytrait.php new file mode 100644 index 000000000000..22f9fe275d8c --- /dev/null +++ b/apps/files_external/lib/prioritytrait.php @@ -0,0 +1,60 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement priority mechanics for a configuration class + */ +trait PriorityTrait { + + /** @var int initial priority */ + protected $priority = BackendService::PRIORITY_DEFAULT; + + /** + * @return int + */ + public function getPriority() { + return $this->priority; + } + + /** + * @param int $priority + * @return self + */ + public function setPriority($priority) { + $this->priority = $priority; + return $this; + } + + /** + * @param PriorityTrait $a + * @param PriorityTrait $b + * @return int + */ + public static function priorityCompare(PriorityTrait $a, PriorityTrait $b) { + return ($a->getPriority() - $b->getPriority()); + } + +} + diff --git a/apps/files_external/lib/sessionstoragewrapper.php b/apps/files_external/lib/sessionstoragewrapper.php new file mode 100644 index 000000000000..91be7eb19030 --- /dev/null +++ b/apps/files_external/lib/sessionstoragewrapper.php @@ -0,0 +1,43 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Files\Storage; +use \OC\Files\Storage\Wrapper\PermissionsMask; +use \OCP\Constants; + +/** + * Wrap Storage in PermissionsMask for session ephemeral use + */ +class SessionStorageWrapper extends PermissionsMask { + + /** + * @param array $arguments ['storage' => $storage] + */ + public function __construct(array $arguments) { + // disable sharing permission + $arguments['mask'] = Constants::PERMISSION_ALL & ~Constants::PERMISSION_SHARE; + parent::__construct($arguments); + } + +} + diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index 7f921b5342ff..921e7283c665 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -40,10 +40,11 @@ class SFTP extends \OC\Files\Storage\Common { private $host; private $user; - private $password; private $root; private $port = 22; + private $auth; + /** * @var SFTP */ @@ -73,8 +74,15 @@ public function __construct($params) { } $this->user = $params['user']; - $this->password - = isset($params['password']) ? $params['password'] : ''; + + if (isset($params['public_key_auth'])) { + $this->auth = $params['public_key_auth']; + } elseif (isset($params['password'])) { + $this->auth = $params['password']; + } else { + throw new \UnexpectedValueException('no authentication parameters specified'); + } + $this->root = isset($params['root']) ? $this->cleanPath($params['root']) : '/'; @@ -112,7 +120,7 @@ public function getConnection() { $this->writeHostKeys($hostKeys); } - if (!$this->client->login($this->user, $this->password)) { + if (!$this->client->login($this->user, $this->auth)) { throw new \Exception('Login failed'); } return $this->client; @@ -125,7 +133,6 @@ public function test() { if ( !isset($this->host) || !isset($this->user) - || !isset($this->password) ) { return false; } diff --git a/apps/files_external/lib/sftp_key.php b/apps/files_external/lib/sftp_key.php deleted file mode 100644 index a193b3236789..000000000000 --- a/apps/files_external/lib/sftp_key.php +++ /dev/null @@ -1,215 +0,0 @@ - - * @author Morris Jobke - * @author Ross Nicoll - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -namespace OC\Files\Storage; - -use phpseclib\Crypt\RSA; - -class SFTP_Key extends \OC\Files\Storage\SFTP { - private $publicKey; - private $privateKey; - - /** - * {@inheritdoc} - */ - public function __construct($params) { - parent::__construct($params); - $this->publicKey = $params['public_key']; - $this->privateKey = $params['private_key']; - } - - /** - * Returns the connection. - * - * @return \phpseclib\Net\SFTP connected client instance - * @throws \Exception when the connection failed - */ - public function getConnection() { - if (!is_null($this->client)) { - return $this->client; - } - - $hostKeys = $this->readHostKeys(); - $this->client = new \phpseclib\Net\SFTP($this->getHost()); - - // The SSH Host Key MUST be verified before login(). - $currentHostKey = $this->client->getServerPublicHostKey(); - if (array_key_exists($this->getHost(), $hostKeys)) { - if ($hostKeys[$this->getHost()] !== $currentHostKey) { - throw new \Exception('Host public key does not match known key'); - } - } else { - $hostKeys[$this->getHost()] = $currentHostKey; - $this->writeHostKeys($hostKeys); - } - - $key = $this->getPrivateKey(); - if (is_null($key)) { - throw new \Exception('Secret key could not be loaded'); - } - if (!$this->client->login($this->getUser(), $key)) { - throw new \Exception('Login failed'); - } - return $this->client; - } - - /** - * Returns the private key to be used for authentication to the remote server. - * - * @return RSA instance or null in case of a failure to load the key. - */ - private function getPrivateKey() { - $key = new RSA(); - $key->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); - if (!$key->loadKey($this->privateKey)) { - // Should this exception rather than return null? - return null; - } - return $key; - } - - /** - * Throws an exception if the provided host name/address is invalid (cannot be resolved - * and is not an IPv4 address). - * - * @return true; never returns in case of a problem, this return value is used just to - * make unit tests happy. - */ - public function assertHostAddressValid($hostname) { - // TODO: Should handle IPv6 addresses too - if (!preg_match('/^\d+\.\d+\.\d+\.\d+$/', $hostname) && gethostbyname($hostname) === $hostname) { - // Hostname is not an IPv4 address and cannot be resolved via DNS - throw new \InvalidArgumentException('Cannot resolve hostname.'); - } - return true; - } - - /** - * Throws an exception if the provided port number is invalid (cannot be resolved - * and is not an IPv4 address). - * - * @return true; never returns in case of a problem, this return value is used just to - * make unit tests happy. - */ - public function assertPortNumberValid($port) { - if (!preg_match('/^\d+$/', $port)) { - throw new \InvalidArgumentException('Port number must be a number.'); - } - if ($port < 0 || $port > 65535) { - throw new \InvalidArgumentException('Port number must be between 0 and 65535 inclusive.'); - } - return true; - } - - /** - * Replaces anything that's not an alphanumeric character or "." in a hostname - * with "_", to make it safe for use as part of a file name. - */ - protected function sanitizeHostName($name) { - return preg_replace('/[^\d\w\._]/', '_', $name); - } - - /** - * Replaces anything that's not an alphanumeric character or "_" in a username - * with "_", to make it safe for use as part of a file name. - */ - protected function sanitizeUserName($name) { - return preg_replace('/[^\d\w_]/', '_', $name); - } - - public function test() { - - // FIXME: Use as expression in empty once PHP 5.4 support is dropped - $host = $this->getHost(); - if (empty($host)) { - \OC::$server->getLogger()->warning('Hostname has not been specified'); - return false; - } - // FIXME: Use as expression in empty once PHP 5.4 support is dropped - $user = $this->getUser(); - if (empty($user)) { - \OC::$server->getLogger()->warning('Username has not been specified'); - return false; - } - if (!isset($this->privateKey)) { - \OC::$server->getLogger()->warning('Private key was missing from the request'); - return false; - } - - // Sanity check the host - $hostParts = explode(':', $this->getHost()); - try { - if (count($hostParts) == 1) { - $hostname = $hostParts[0]; - $this->assertHostAddressValid($hostname); - } else if (count($hostParts) == 2) { - $hostname = $hostParts[0]; - $this->assertHostAddressValid($hostname); - $this->assertPortNumberValid($hostParts[1]); - } else { - throw new \Exception('Host connection string is invalid.'); - } - } catch(\Exception $e) { - \OC::$server->getLogger()->warning($e->getMessage()); - return false; - } - - // Validate the key - $key = $this->getPrivateKey(); - if (is_null($key)) { - \OC::$server->getLogger()->warning('Secret key could not be loaded'); - return false; - } - - try { - if ($this->getConnection()->nlist() === false) { - return false; - } - } catch(\Exception $e) { - // We should be throwing a more specific error, so we're not just catching - // Exception here - \OC::$server->getLogger()->warning($e->getMessage()); - return false; - } - - // Save the key somewhere it can easily be extracted later - if (\OC::$server->getUserSession()->getUser()) { - $view = new \OC\Files\View('/'.\OC::$server->getUserSession()->getUser()->getUId().'/files_external/sftp_keys'); - if (!$view->is_dir('')) { - if (!$view->mkdir('')) { - \OC::$server->getLogger()->warning('Could not create secret key directory.'); - return false; - } - } - $key_filename = $this->sanitizeUserName($this->getUser()).'@'.$this->sanitizeHostName($hostname).'.pub'; - $key_file = $view->fopen($key_filename, "w"); - if ($key_file) { - fwrite($key_file, $this->publicKey); - fclose($key_file); - } else { - \OC::$server->getLogger()->warning('Could not write secret key file.'); - } - } - - return true; - } -} diff --git a/apps/files_external/lib/smb_oc.php b/apps/files_external/lib/smb_oc.php deleted file mode 100644 index 52b73c46ff9d..000000000000 --- a/apps/files_external/lib/smb_oc.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OC\Files\Storage; - - -use Icewind\SMB\Exception\AccessDeniedException; -use Icewind\SMB\Exception\Exception; -use Icewind\SMB\Server; - -class SMB_OC extends SMB { - private $username_as_share; - - /** - * @param array $params - * @throws \Exception - */ - public function __construct($params) { - if (isset($params['host'])) { - $host = $params['host']; - $this->username_as_share = ($params['username_as_share'] === 'true'); - - // dummy credentials, unused, to satisfy constructor - $user = 'foo'; - $password = 'bar'; - if (\OC::$server->getSession()->exists('smb-credentials')) { - $params_auth = json_decode(\OC::$server->getCrypto()->decrypt(\OC::$server->getSession()->get('smb-credentials')), true); - $user = \OC::$server->getSession()->get('loginname'); - $password = $params_auth['password']; - } else { - // assume we are testing from the admin section - } - - $root = isset($params['root']) ? $params['root'] : '/'; - $share = ''; - - if ($this->username_as_share) { - $share = '/' . $user; - } elseif (isset($params['share'])) { - $share = $params['share']; - } else { - throw new \Exception(); - } - parent::__construct(array( - "user" => $user, - "password" => $password, - "host" => $host, - "share" => $share, - "root" => $root - )); - } else { - throw new \Exception(); - } - } - - - /** - * Intercepts the user credentials on login and stores them - * encrypted inside the session if SMB_OC storage is enabled. - * @param array $params - */ - public static function login($params) { - $mountpoints = \OC_Mount_Config::getAbsoluteMountPoints($params['uid']); - $mountpointClasses = array(); - foreach($mountpoints as $mountpoint) { - $mountpointClasses[$mountpoint['class']] = true; - } - if(isset($mountpointClasses['\OC\Files\Storage\SMB_OC'])) { - \OC::$server->getSession()->set('smb-credentials', \OC::$server->getCrypto()->encrypt(json_encode($params))); - } - } - - /** - * @param string $path - * @return boolean - */ - public function isSharable($path) { - return false; - } - - /** - * @param bool $isPersonal - * @return bool - */ - public function test($isPersonal = true) { - if ($isPersonal) { - if ($this->stat('')) { - return true; - } - return false; - } else { - $server = new Server($this->server->getHost(), '', ''); - - try { - $server->listShares(); - return true; - } catch (AccessDeniedException $e) { - // expected due to anonymous login - return true; - } catch (Exception $e) { - return false; - } - } - } -} diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php index 92c27701d803..aeb8f5270787 100644 --- a/apps/files_external/lib/storageconfig.php +++ b/apps/files_external/lib/storageconfig.php @@ -21,6 +21,9 @@ namespace OCA\Files_external\Lib; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + /** * External storage configuration */ @@ -34,11 +37,18 @@ class StorageConfig implements \JsonSerializable { private $id; /** - * Backend class name + * Backend * - * @var string + * @var Backend */ - private $backendClass; + private $backend; + + /** + * Authentication mechanism + * + * @var AuthMechanism + */ + private $authMechanism; /** * Backend options @@ -138,21 +148,31 @@ public function setMountPoint($mountPoint) { } /** - * Returns the external storage backend class name - * - * @return string external storage backend class name + * @return Backend */ - public function getBackendClass() { - return $this->backendClass; + public function getBackend() { + return $this->backend; } /** - * Sets the external storage backend class name - * - * @param string external storage backend class name + * @param Backend + */ + public function setBackend(Backend $backend) { + $this->backend= $backend; + } + + /** + * @return AuthMechanism */ - public function setBackendClass($backendClass) { - $this->backendClass = $backendClass; + public function getAuthMechanism() { + return $this->authMechanism; + } + + /** + * @param AuthMechanism + */ + public function setAuthMechanism(AuthMechanism $authMechanism) { + $this->authMechanism = $authMechanism; } /** @@ -173,6 +193,25 @@ public function setBackendOptions($backendOptions) { $this->backendOptions = $backendOptions; } + /** + * @param string $key + * @return mixed + */ + public function getBackendOption($key) { + if (isset($this->backendOptions[$key])) { + return $this->backendOptions[$key]; + } + return null; + } + + /** + * @param string $key + * @param mixed $value + */ + public function setBackendOption($key, $value) { + $this->backendOptions[$key] = $value; + } + /** * Returns the mount priority * @@ -283,7 +322,8 @@ public function jsonSerialize() { $result['id'] = $this->id; } $result['mountPoint'] = $this->mountPoint; - $result['backendClass'] = $this->backendClass; + $result['backend'] = $this->backend->getIdentifier(); + $result['authMechanism'] = $this->authMechanism->getIdentifier(); $result['backendOptions'] = $this->backendOptions; if (!is_null($this->priority)) { $result['priority'] = $this->priority; diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php new file mode 100644 index 000000000000..4f4ea1f6ff24 --- /dev/null +++ b/apps/files_external/lib/storagemodifiertrait.php @@ -0,0 +1,57 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCP\Files\Storage; +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; +use \OCP\Files\StorageNotAvailableException; + +/** + * Trait for objects that can modify StorageConfigs and wrap Storages + */ +trait StorageModifierTrait { + + /** + * Modify a StorageConfig parameters + * + * @param StorageConfig $storage + * @throws InsufficientDataForMeaningfulAnswerException + * @throws StorageNotAvailableException + */ + public function manipulateStorageConfig(StorageConfig &$storage) { + } + + /** + * Wrap a Storage if necessary + * + * @param Storage $storage + * @return Storage + * @throws InsufficientDataForMeaningfulAnswerException + * @throws StorageNotAvailableException + */ + public function wrapStorage(Storage $storage) { + return $storage; + } + +} + diff --git a/apps/files_external/lib/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php new file mode 100644 index 000000000000..06c95dd70c91 --- /dev/null +++ b/apps/files_external/lib/visibilitytrait.php @@ -0,0 +1,129 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement visibility mechanics for a configuration class + */ +trait VisibilityTrait { + + /** @var int visibility */ + protected $visibility = BackendService::VISIBILITY_DEFAULT; + + /** @var int allowed visibilities */ + protected $allowedVisibility = BackendService::VISIBILITY_DEFAULT; + + /** + * @return int + */ + public function getVisibility() { + return $this->visibility; + } + + /** + * Check if the backend is visible for a user type + * + * @param int $visibility + * @return bool + */ + public function isVisibleFor($visibility) { + if ($this->visibility & $visibility) { + return true; + } + return false; + } + + /** + * @param int $visibility + * @return self + */ + public function setVisibility($visibility) { + $this->visibility = $visibility; + $this->allowedVisibility |= $visibility; + return $this; + } + + /** + * @param int $visibility + * @return self + */ + public function addVisibility($visibility) { + return $this->setVisibility($this->visibility | $visibility); + } + + /** + * @param int $visibility + * @return self + */ + public function removeVisibility($visibility) { + return $this->setVisibility($this->visibility & ~$visibility); + } + + /** + * @return int + */ + public function getAllowedVisibility() { + return $this->allowedVisibility; + } + + /** + * Check if the backend is allowed to be visible for a user type + * + * @param int $allowedVisibility + * @return bool + */ + public function isAllowedVisibleFor($allowedVisibility) { + if ($this->allowedVisibility & $allowedVisibility) { + return true; + } + return false; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function setAllowedVisibility($allowedVisibility) { + $this->allowedVisibility = $allowedVisibility; + $this->visibility &= $allowedVisibility; + return $this; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function addAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility | $allowedVisibility); + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function removeAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility & ~$allowedVisibility); + } + +} diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 1ac0f65a1f05..ac0ceacfdb7a 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -24,34 +24,34 @@ * */ +use \OCA\Files_External\Service\BackendService; + +// we must use the same container +$appContainer = \OC_Mount_Config::$appContainer; +$backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); +$userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService'); + OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); -$backends = OC_Mount_Config::getPersonalBackends(); -$mounts = OC_Mount_Config::getPersonalMountPoints(); -$hasId = true; -foreach ($mounts as $mount) { - if (!isset($mount['id'])) { - // some mount points are missing ids - $hasId = false; - break; +$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL); +$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_PERSONAL); +foreach ($backends as $backend) { + if ($backend->getCustomJs()) { + \OCP\Util::addScript('files_external', $backend->getCustomJs()); } } - -if (!$hasId) { - $service = new \OCA\Files_external\Service\UserStoragesService(\OC::$server->getUserSession()); - // this will trigger the new storage code which will automatically - // generate storage config ids - $service->getAllStorages(); - // re-read updated config - $mounts = OC_Mount_Config::getPersonalMountPoints(); - // TODO: use the new storage config format in the template +foreach ($authMechanisms as $authMechanism) { + if ($authMechanism->getCustomJs()) { + \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); + } } $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', false); -$tmpl->assign('mounts', $mounts); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); +$tmpl->assign('storages', $userStoragesService->getAllStorages()); +$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('backends', $backends); +$tmpl->assign('authMechanisms', $authMechanisms); return $tmpl->fetchPage(); diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php new file mode 100644 index 000000000000..bee08ecbd159 --- /dev/null +++ b/apps/files_external/service/backendservice.php @@ -0,0 +1,275 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCP\IConfig; + +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; + +/** + * 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 AuthMechanism[] */ + private $authMechanisms = []; + + /** + * @param IConfig $config + */ + public function __construct( + IConfig $config + ) { + $this->config = $config; + + // Load config values + if ($this->config->getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') { + $this->userMountingAllowed = false; + } + $this->userMountingBackends = explode(',', + $this->config->getAppValue('files_external', 'user_mounting_backends', '') + ); + } + + /** + * Register a backend + * + * @param Backend $backend + */ + public function registerBackend(Backend $backend) { + if (!$this->isAllowedUserBackend($backend)) { + $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL); + } + foreach ($backend->getIdentifierAliases() as $alias) { + $this->backends[$alias] = $backend; + } + } + + /** + * @param Backend[] $backends + */ + public function registerBackends(array $backends) { + foreach ($backends as $backend) { + $this->registerBackend($backend); + } + } + /** + * Register an authentication mechanism + * + * @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; + } + } + + /** + * @param AuthMechanism[] $mechanisms + */ + public function registerAuthMechanisms(array $mechanisms) { + foreach ($mechanisms as $mechanism) { + $this->registerAuthMechanism($mechanism); + } + } + + /** + * Get all backends + * + * @return Backend[] + */ + public function getBackends() { + // 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 empty($backend->checkDependencies()); + }); + } + + /** + * Get backends visible for $visibleFor + * + * @param int $visibleFor + * @return Backend[] + */ + public function getBackendsVisibleFor($visibleFor) { + return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) { + return $backend->isVisibleFor($visibleFor); + }); + } + + /** + * Get backends allowed to be visible for $visibleFor + * + * @param int $visibleFor + * @return Backend[] + */ + public function getBackendsAllowedVisibleFor($visibleFor) { + return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) { + return $backend->isAllowedVisibleFor($visibleFor); + }); + } + + /** + * @param string $identifier + * @return Backend|null + */ + public function getBackend($identifier) { + if (isset($this->backends[$identifier])) { + return $this->backends[$identifier]; + } + return null; + } + + /** + * Get all authentication mechanisms + * + * @return AuthMechanism[] + */ + public function getAuthMechanisms() { + // 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); + }); + } + + /** + * Get authentication mechanisms visible for $visibleFor + * + * @param int $visibleFor + * @return AuthMechanism[] + */ + public function getAuthMechanismsVisibleFor($visibleFor) { + return array_filter($this->getAuthMechanisms(), function($authMechanism) use ($visibleFor) { + return $authMechanism->isVisibleFor($visibleFor); + }); + } + + /** + * Get authentication mechanisms allowed to be visible for $visibleFor + * + * @param int $visibleFor + * @return AuthMechanism[] + */ + public function getAuthMechanismsAllowedVisibleFor($visibleFor) { + return array_filter($this->getAuthMechanisms(), function($authMechanism) use ($visibleFor) { + return $authMechanism->isAllowedVisibleFor($visibleFor); + }); + } + + + /** + * @param string $identifier + * @return AuthMechanism|null + */ + public function getAuthMechanism($identifier) { + 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 && + !empty(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/service/globalstoragesservice.php b/apps/files_external/service/globalstoragesservice.php index 04445127b34e..0e2d3f2b9c11 100644 --- a/apps/files_external/service/globalstoragesservice.php +++ b/apps/files_external/service/globalstoragesservice.php @@ -92,7 +92,7 @@ public function writeConfig($storages) { $storageConfig->setBackendOptions($oldBackendOptions); } - \OC_Mount_Config::writeData(null, $mountPoints); + $this->writeLegacyConfig($mountPoints); } /** diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 930f994455e2..621e74324868 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -28,12 +28,23 @@ use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Service\BackendService; /** * Service class to manage external storages */ abstract class StoragesService { + /** @var BackendService */ + protected $backendService; + + /** + * @param BackendService $backendService + */ + public function __construct(BackendService $backendService) { + $this->backendService = $backendService; + } + /** * Read legacy config data * @@ -44,6 +55,16 @@ protected function readLegacyConfig() { return \OC_Mount_Config::readData(); } + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + // write global config + \OC_Mount_Config::writeData(null, $mountPoints); + } + /** * Copy legacy storage options into the given storage config object. * @@ -60,14 +81,31 @@ protected function populateStorageConfigWithLegacyOptions( $applicable, $storageOptions ) { - $storageConfig->setBackendClass($storageOptions['class']); + $backend = $this->backendService->getBackend($storageOptions['backend']); + if (!$backend) { + throw new \UnexpectedValueException('Invalid backend '.$storageOptions['backend']); + } + $storageConfig->setBackend($backend); + + if (isset($storageOptions['authMechanism'])) { + $authMechanism = $this->backendService->getAuthMechanism($storageOptions['authMechanism']); + } else { + $authMechanism = $backend->getLegacyAuthMechanism($storageOptions); + $storageOptions['authMechanism'] = 'null'; // to make error handling easier + } + if (!$authMechanism) { + throw new \UnexpectedValueException('Invalid authentication mechanism '.$storageOptions['authMechanism']); + } + $storageConfig->setAuthMechanism($authMechanism); + $storageConfig->setBackendOptions($storageOptions['options']); if (isset($storageOptions['mountOptions'])) { $storageConfig->setMountOptions($storageOptions['mountOptions']); } - if (isset($storageOptions['priority'])) { - $storageConfig->setPriority($storageOptions['priority']); + if (!isset($storageOptions['priority'])) { + $storageOptions['priority'] = $backend->getPriority(); } + $storageConfig->setPriority($storageOptions['priority']); if ($mountType === \OC_Mount_Config::MOUNT_TYPE_USER) { $applicableUsers = $storageConfig->getApplicableUsers(); @@ -102,8 +140,10 @@ protected function readConfig() { * - $mountPath is the mount point path (where the storage must be mounted) * - $storageOptions is a map of storage options: * - "priority": storage priority - * - "backend": backend class name + * - "backend": backend identifier + * - "class": LEGACY backend class name * - "options": backend-specific options + * - "authMechanism": authentication mechanism identifier * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc) */ @@ -147,6 +187,13 @@ protected function readConfig() { // 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])) { @@ -222,7 +269,9 @@ protected function addMountPoint(&$mountPoints, $mountType, $applicable, $rootMo $options = [ 'id' => $storageConfig->getId(), - 'class' => $storageConfig->getBackendClass(), + 'backend' => $storageConfig->getBackend()->getIdentifier(), + //'class' => $storageConfig->getBackend()->getClass(), + 'authMechanism' => $storageConfig->getAuthMechanism()->getIdentifier(), 'options' => $storageConfig->getBackendOptions(), ]; @@ -296,6 +345,59 @@ public function addStorage(StorageConfig $newStorage) { 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 * diff --git a/apps/files_external/service/userglobalstoragesservice.php b/apps/files_external/service/userglobalstoragesservice.php new file mode 100644 index 000000000000..6699922b7f79 --- /dev/null +++ b/apps/files_external/service/userglobalstoragesservice.php @@ -0,0 +1,94 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCA\Files_external\Service\GlobalStoragesService; +use \OCA\Files_External\Service\BackendService; +use \OCP\IUserSession; +use \OCP\IGroupManager; +use \OCA\Files_External\Service\UserTrait; + +/** + * Service class to read global storages applicable to the user + * Read-only access available, attempting to write will throw DomainException + */ +class UserGlobalStoragesService extends GlobalStoragesService { + + use UserTrait; + + /** @var IGroupManager */ + protected $groupManager; + + /** + * @param BackendService $backendService + * @param IUserSession $userSession + * @param IGroupManager $groupManager + */ + public function __construct( + BackendService $backendService, + IUserSession $userSession, + IGroupManager $groupManager + ) { + parent::__construct($backendService); + $this->userSession = $userSession; + $this->groupManager = $groupManager; + } + + /** + * Read legacy config data + * + * @return array list of mount configs + */ + protected function readLegacyConfig() { + // read global config + $data = parent::readLegacyConfig(); + $userId = $this->getUser()->getUID(); + + if (isset($data[\OC_Mount_Config::MOUNT_TYPE_USER])) { + $data[\OC_Mount_Config::MOUNT_TYPE_USER] = array_filter( + $data[\OC_Mount_Config::MOUNT_TYPE_USER], function($key) use ($userId) { + return (strtolower($key) === strtolower($userId) || $key === 'all'); + }, ARRAY_FILTER_USE_KEY + ); + } + + if (isset($data[\OC_Mount_Config::MOUNT_TYPE_GROUP])) { + $data[\OC_Mount_Config::MOUNT_TYPE_GROUP] = array_filter( + $data[\OC_Mount_Config::MOUNT_TYPE_GROUP], function($key) use ($userId) { + return ($this->groupManager->isInGroup($userId, $key)); + }, ARRAY_FILTER_USE_KEY + ); + } + + return $data; + } + + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + throw new \DomainException('UserGlobalStoragesService writing disallowed'); + } + +} diff --git a/apps/files_external/service/userstoragesservice.php b/apps/files_external/service/userstoragesservice.php index 2f2556043fe7..c69b8d4f51e4 100644 --- a/apps/files_external/service/userstoragesservice.php +++ b/apps/files_external/service/userstoragesservice.php @@ -26,6 +26,8 @@ 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 @@ -33,22 +35,20 @@ */ class UserStoragesService extends StoragesService { - /** - * User session - * - * @var IUserSession - */ - private $userSession; + use UserTrait; /** * Create a user storages service * + * @param BackendService $backendService * @param IUserSession $userSession user session */ public function __construct( + BackendService $backendService, IUserSession $userSession ) { $this->userSession = $userSession; + parent::__construct($backendService); } /** @@ -58,17 +58,28 @@ public function __construct( */ protected function readLegacyConfig() { // read user config - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); return \OC_Mount_Config::readData($user); } + /** + * Write legacy config data + * + * @param array $mountPoints + */ + protected function writeLegacyConfig(array $mountPoints) { + // write user config + $user = $this->getUser()->getUID(); + \OC_Mount_Config::writeData($user, $mountPoints); + } + /** * Read the external storages config * * @return array map of storage id to storage config */ protected function readConfig() { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // TODO: in the future don't rely on the global config reading code $storages = parent::readConfig(); @@ -95,7 +106,7 @@ protected function readConfig() { * @param array $storages map of storage id to storage config */ public function writeConfig($storages) { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // let the horror begin $mountPoints = []; @@ -123,7 +134,7 @@ public function writeConfig($storages) { $storageConfig->setBackendOptions($oldBackendOptions); } - \OC_Mount_Config::writeData($user, $mountPoints); + $this->writeLegacyConfig($mountPoints); } /** @@ -134,7 +145,7 @@ public function writeConfig($storages) { * @param string $signal signal to trigger */ protected function triggerHooks(StorageConfig $storage, $signal) { - $user = $this->userSession->getUser()->getUID(); + $user = $this->getUser()->getUID(); // trigger hook for the current user $this->triggerApplicableHooks( diff --git a/apps/files_external/service/usertrait.php b/apps/files_external/service/usertrait.php new file mode 100644 index 000000000000..4f84543565cf --- /dev/null +++ b/apps/files_external/service/usertrait.php @@ -0,0 +1,74 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Service; + +use \OCP\IUserSession; +use \OCP\IUser; + +/** + * Trait for getting user information in a service + */ +trait UserTrait { + + /** @var IUserSession */ + protected $userSession; + + /** + * User override + * + * @var IUser|null + */ + private $user = null; + + /** + * @return IUser|null + */ + protected function getUser() { + if ($this->user) { + return $this->user; + } + return $this->userSession->getUser(); + } + + /** + * Override the user from the session + * Unset with ->resetUser() when finished! + * + * @param IUser + * @return self + */ + public function setUser(IUser $user) { + $this->user = $user; + return $this; + } + + /** + * Reset the user override + * + * @return self + */ + public function resetUser() { + $this->user = null; + return $this; + } +} + diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 6db68713d980..621ffd4beb10 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -26,54 +26,41 @@ * */ +use \OCA\Files_External\Service\BackendService; + OC_Util::checkAdminUser(); +// we must use the same container +$appContainer = \OC_Mount_Config::$appContainer; +$backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); +$globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService'); + OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); -$backends = OC_Mount_Config::getBackends(); -$personal_backends = array(); -$enabled_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); -foreach ($backends as $class => $backend) -{ - if ($class != '\OC\Files\Storage\Local') - { - $personal_backends[$class] = array( - 'backend' => $backend['backend'], - 'enabled' => in_array($class, $enabled_backends), - ); +$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN); +$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_ADMIN); +foreach ($backends as $backend) { + if ($backend->getCustomJs()) { + \OCP\Util::addScript('files_external', $backend->getCustomJs()); } } - -$mounts = OC_Mount_Config::getSystemMountPoints(); -$hasId = true; -foreach ($mounts as $mount) { - if (!isset($mount['id'])) { - // some mount points are missing ids - $hasId = false; - break; +foreach ($authMechanisms as $authMechanism) { + if ($authMechanism->getCustomJs()) { + \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); } } -if (!$hasId) { - $service = new \OCA\Files_external\Service\GlobalStoragesService(); - // this will trigger the new storage code which will automatically - // generate storage config ids - $service->getAllStorages(); - // re-read updated config - $mounts = OC_Mount_Config::getSystemMountPoints(); - // TODO: use the new storage config format in the template -} - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', true); -$tmpl->assign('mounts', $mounts); +$tmpl->assign('storages', $globalStoragesService->getAllStorages()); $tmpl->assign('backends', $backends); -$tmpl->assign('personal_backends', $personal_backends); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); -$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes')); +$tmpl->assign('authMechanisms', $authMechanisms); +$tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL)); +$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); +$tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); return $tmpl->fetchPage(); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index b886c2e1b1b2..611c5807a5f8 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -1,3 +1,58 @@ +getName()])) { + $value = $options[$parameter->getName()]; + } + $placeholder = $parameter->getText(); + $is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL); + + switch ($parameter->getType()) { + case DefinitionParameter::VALUE_PASSWORD: ?> + + class="" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> + + + + class="" + data-parameter="getName()); ?>" + value="" + /> + + + class="" + data-parameter="getName()); ?>" + value="" + placeholder="" + /> +

t('External Storage')); ?>

'')) print_unescaped(''.$_['dependencies'].''); ?> @@ -7,6 +62,7 @@ t('Folder name')); ?> t('External storage')); ?> + t('Authentication')); ?> t('Configuration')); ?> '.$l->t('Available for').''); ?>   @@ -14,103 +70,120 @@ - array('id' => ''))); ?> - - data-id=""> + + - - - - - - - - - - - $value): ?> - - - - class="optional" - data-parameter="" - value="" - placeholder="" /> - - - - - - class="optional" - data-parameter="" - value="" - placeholder="" /> - - + getBackend()->getText()); ?> + + + - - ' - data-applicable-users=''> - + + getBackendOptions(); + foreach ($storage->getBackend()->getParameters() as $parameter) { + writeParameterInput($parameter, $options); + } + foreach ($storage->getAuthMechanism()->getParameters() as $parameter) { + writeParameterInput($parameter, $options, ['auth-param']); + } + ?> + + + + - + <?php p($l->t('Advanced settings')); ?> - + src="" + /> + - - - + - class="remove" - class="hidden" - ><?php p($l->t('Delete')); ?> + + <?php p($l->t('Delete')); ?> + + + + + + + + + + + '> + + + + + + + + <?php p($l->t('Advanced settings')); ?> + + + + <?php p($l->t('Delete')); ?> + +
@@ -123,9 +196,9 @@ class="svg action"

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

diff --git a/apps/files_external/tests/auth/authmechanismtest.php b/apps/files_external/tests/auth/authmechanismtest.php new file mode 100644 index 000000000000..b09d65a02dfa --- /dev/null +++ b/apps/files_external/tests/auth/authmechanismtest.php @@ -0,0 +1,80 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Auth; + +class AuthMechanismTest extends \Test\TestCase { + + public function testJsonSerialization() { + $mechanism = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->setMethods(['jsonSerializeDefinition']) + ->getMock(); + $mechanism->expects($this->once()) + ->method('jsonSerializeDefinition') + ->willReturn(['foo' => 'bar']); + + $mechanism->setScheme('scheme'); + + $json = $mechanism->jsonSerialize(); + $this->assertEquals('bar', $json['foo']); + $this->assertEquals('scheme', $json['scheme']); + } + + public function validateStorageProvider() { + return [ + [true, 'scheme', true], + [false, 'scheme', false], + [true, 'foobar', true], + [false, 'barfoo', true], + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $scheme, $definitionSuccess) { + $mechanism = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->setMethods(['validateStorageDefinition']) + ->getMock(); + $mechanism->expects($this->atMost(1)) + ->method('validateStorageDefinition') + ->willReturn($definitionSuccess); + + $mechanism->setScheme($scheme); + + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->once()) + ->method('getAuthSchemes') + ->willReturn(['scheme' => true, 'foobar' => true]); + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + $storageConfig->expects($this->once()) + ->method('getBackend') + ->willReturn($backend); + + $this->assertEquals($expectedSuccess, $mechanism->validateStorage($storageConfig)); + } + +} diff --git a/apps/files_external/tests/backend/backendtest.php b/apps/files_external/tests/backend/backendtest.php new file mode 100644 index 000000000000..4956a923e94d --- /dev/null +++ b/apps/files_external/tests/backend/backendtest.php @@ -0,0 +1,89 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Backend; + +use \OCA\Files_External\Lib\Backend\Backend; + +class BackendTest extends \Test\TestCase { + + public function testJsonSerialization() { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->setMethods(['jsonSerializeDefinition']) + ->getMock(); + $backend->expects($this->once()) + ->method('jsonSerializeDefinition') + ->willReturn(['foo' => 'bar', 'name' => 'abc']); + + $backend->setPriority(57); + $backend->addAuthScheme('foopass'); + $backend->addAuthScheme('barauth'); + + $json = $backend->jsonSerialize(); + $this->assertEquals('bar', $json['foo']); + $this->assertEquals('abc', $json['name']); + $this->assertEquals($json['name'], $json['backend']); + $this->assertEquals(57, $json['priority']); + + $this->assertContains('foopass', $json['authSchemes']); + $this->assertContains('barauth', $json['authSchemes']); + } + + public function validateStorageProvider() { + return [ + [true, true], + [false, false], + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $definitionSuccess) { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->setMethods(['validateStorageDefinition']) + ->getMock(); + $backend->expects($this->atMost(1)) + ->method('validateStorageDefinition') + ->willReturn($definitionSuccess); + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + + $this->assertEquals($expectedSuccess, $backend->validateStorage($storageConfig)); + } + + public function testLegacyAuthMechanismCallback() { + $backend = new Backend(); + $backend->setLegacyAuthMechanismCallback(function(array $params) { + if (isset($params['ping'])) { + return 'pong'; + } + return 'foobar'; + }); + + $this->assertEquals('pong', $backend->getLegacyAuthMechanism(['ping' => true])); + $this->assertEquals('foobar', $backend->getLegacyAuthMechanism(['other' => true])); + $this->assertEquals('foobar', $backend->getLegacyAuthMechanism()); + } + +} diff --git a/apps/files_external/tests/backend/legacybackendtest.php b/apps/files_external/tests/backend/legacybackendtest.php new file mode 100644 index 000000000000..44cb16a4986f --- /dev/null +++ b/apps/files_external/tests/backend/legacybackendtest.php @@ -0,0 +1,81 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests\Backend; + +use \OCA\Files_External\Lib\Backend\LegacyBackend; +use \OCA\Files_External\Lib\DefinitionParameter; + +class LegacyBackendTest extends \Test\TestCase { + + public function testConstructor() { + $auth = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\Builtin') + ->disableOriginalConstructor() + ->getMock(); + + $class = '\OC\Files\Storage\SMB'; + $definition = [ + 'configuration' => [ + 'textfield' => 'Text field', + 'passwordfield' => '*Password field', + 'checkbox' => '!Checkbox', + 'hiddenfield' => '#Hidden field', + 'optionaltext' => '&Optional text field', + 'optionalpassword' => '&*Optional password field', + ], + 'backend' => 'Backend text', + 'priority' => 123, + 'custom' => 'foo/bar.js', + 'has_dependencies' => true, + ]; + + $backend = new LegacyBackend($class, $definition, $auth); + + $this->assertEquals('\OC\Files\Storage\SMB', $backend->getStorageClass()); + $this->assertEquals('Backend text', $backend->getText()); + $this->assertEquals(123, $backend->getPriority()); + $this->assertEquals('foo/bar.js', $backend->getCustomJs()); + $this->assertEquals(true, $backend->hasDependencies()); + $this->assertArrayHasKey('builtin', $backend->getAuthSchemes()); + $this->assertEquals($auth, $backend->getLegacyAuthMechanism()); + + $parameters = $backend->getParameters(); + $this->assertEquals('Text field', $parameters['textfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_TEXT, $parameters['textfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['textfield']->getFlags()); + $this->assertEquals('Password field', $parameters['passwordfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_PASSWORD, $parameters['passwordfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['passwordfield']->getFlags()); + $this->assertEquals('Checkbox', $parameters['checkbox']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_BOOLEAN, $parameters['checkbox']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['checkbox']->getFlags()); + $this->assertEquals('Hidden field', $parameters['hiddenfield']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_HIDDEN, $parameters['hiddenfield']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_NONE, $parameters['hiddenfield']->getFlags()); + $this->assertEquals('Optional text field', $parameters['optionaltext']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_TEXT, $parameters['optionaltext']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_OPTIONAL, $parameters['optionaltext']->getFlags()); + $this->assertEquals('Optional password field', $parameters['optionalpassword']->getText()); + $this->assertEquals(DefinitionParameter::VALUE_PASSWORD, $parameters['optionalpassword']->getType()); + $this->assertEquals(DefinitionParameter::FLAG_OPTIONAL, $parameters['optionalpassword']->getFlags()); + } + +} diff --git a/apps/files_external/tests/controller/globalstoragescontrollertest.php b/apps/files_external/tests/controller/globalstoragescontrollertest.php index fc58743454a8..e1bfad8caf65 100644 --- a/apps/files_external/tests/controller/globalstoragescontrollertest.php +++ b/apps/files_external/tests/controller/globalstoragescontrollertest.php @@ -28,7 +28,9 @@ class GlobalStoragesControllerTest extends StoragesControllerTest { public function setUp() { parent::setUp(); - $this->service = $this->getMock('\OCA\Files_external\Service\GlobalStoragesService'); + $this->service = $this->getMockBuilder('\OCA\Files_external\Service\GlobalStoragesService') + ->disableOriginalConstructor() + ->getMock(); $this->controller = new GlobalStoragesController( 'files_external', diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index 86874ef9786a..2b0ee7a14eaf 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -1,6 +1,7 @@ + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -47,10 +48,48 @@ public function tearDown() { \OC_Mount_Config::$skipTest = false; } + protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getStorageClass') + ->willReturn($storageClass); + $backend->method('getIdentifier') + ->willReturn('identifier:'.$class); + return $backend; + } + + protected function getAuthMechMock($scheme = 'null', $class = '\OCA\Files_External\Lib\Auth\NullMechanism') { + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getScheme') + ->willReturn($scheme); + $authMech->method('getIdentifier') + ->willReturn('identifier:'.$class); + + return $authMech; + } + public function testAddStorage() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('addStorage') ->will($this->returnValue($storageConfig)); @@ -58,6 +97,7 @@ public function testAddStorage() { $response = $this->controller->create( 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -66,14 +106,29 @@ public function testAddStorage() { ); $data = $response->getData(); - $this->assertEquals($storageConfig, $data); $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + $this->assertEquals($storageConfig, $data); } public function testUpdateStorage() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('updateStorage') ->will($this->returnValue($storageConfig)); @@ -82,6 +137,7 @@ public function testUpdateStorage() { 1, 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -90,8 +146,8 @@ public function testUpdateStorage() { ); $data = $response->getData(); - $this->assertEquals($storageConfig, $data); $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + $this->assertEquals($storageConfig, $data); } function mountPointNamesProvider() { @@ -106,6 +162,15 @@ function mountPointNamesProvider() { * @dataProvider mountPointNamesProvider */ public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) { + $storageConfig = new StorageConfig(1); + $storageConfig->setMountPoint($mountPoint); + $storageConfig->setBackend($this->getBackendMock()); + $storageConfig->setAuthMechanism($this->getAuthMechMock()); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -114,6 +179,7 @@ public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) { $response = $this->controller->create( $mountPoint, '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -127,6 +193,7 @@ public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) { 1, $mountPoint, '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -138,6 +205,9 @@ public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) { } public function testAddOrUpdateStorageInvalidBackend() { + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->throwException(new \InvalidArgumentException())); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -146,6 +216,7 @@ public function testAddOrUpdateStorageInvalidBackend() { $response = $this->controller->create( 'mount', '\OC\Files\Storage\InvalidStorage', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -159,6 +230,7 @@ public function testAddOrUpdateStorageInvalidBackend() { 1, 'mount', '\OC\Files\Storage\InvalidStorage', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -170,6 +242,24 @@ public function testAddOrUpdateStorageInvalidBackend() { } public function testUpdateStorageNonExisting() { + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->willReturn(true); + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn(true); + $backend->method('isVisibleFor') + ->willReturn(true); + + $storageConfig = new StorageConfig(255); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->once()) ->method('updateStorage') ->will($this->throwException(new NotFoundException())); @@ -178,6 +268,7 @@ public function testUpdateStorageNonExisting() { 255, 'mount', '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', array(), [], [], @@ -206,9 +297,12 @@ public function testDeleteStorageNonExisting() { } public function testGetStorage() { + $backend = $this->getBackendMock(); + $authMech = $this->getAuthMechMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); - $storageConfig->setBackendClass('\OC\Files\Storage\SMB'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions(['user' => 'test', 'password', 'password123']); $storageConfig->setMountOptions(['priority' => false]); @@ -221,4 +315,66 @@ public function testGetStorage() { $this->assertEquals(Http::STATUS_OK, $response->getStatus()); $this->assertEquals($storageConfig, $response->getData()); } + + public function validateStorageProvider() { + return [ + [true, true, true], + [false, true, false], + [true, false, false], + [false, false, false] + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($backendValidate, $authMechValidate, $expectSuccess) { + $backend = $this->getBackendMock(); + $backend->method('validateStorage') + ->willReturn($backendValidate); + $backend->method('isVisibleFor') + ->willReturn(true); + + $authMech = $this->getAuthMechMock(); + $authMech->method('validateStorage') + ->will($this->returnValue($authMechValidate)); + + $storageConfig = new StorageConfig(); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->once()) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); + + if ($expectSuccess) { + $this->service->expects($this->once()) + ->method('addStorage') + ->with($storageConfig) + ->will($this->returnValue($storageConfig)); + } else { + $this->service->expects($this->never()) + ->method('addStorage'); + } + + $response = $this->controller->create( + 'mount', + '\OC\Files\Storage\SMB', + '\OCA\Files_External\Lib\Auth\NullMechanism', + array(), + [], + [], + [], + null + ); + + if ($expectSuccess) { + $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + } else { + $this->assertEquals(Http::STATUS_UNPROCESSABLE_ENTITY, $response->getStatus()); + } + } + } diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php index f9b4c5b2681d..9f1a8df8d2e8 100644 --- a/apps/files_external/tests/controller/userstoragescontrollertest.php +++ b/apps/files_external/tests/controller/userstoragescontrollertest.php @@ -24,6 +24,8 @@ use \OCA\Files_external\Service\UserStoragesService; use \OCP\AppFramework\Http; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Lib\StorageConfig; +use \OCA\Files_External\Service\BackendService; class UserStoragesControllerTest extends StoragesControllerTest { @@ -44,41 +46,24 @@ public function setUp() { $this->getMock('\OCP\IL10N'), $this->service ); - - $config = \OC::$server->getConfig(); - - $this->oldAllowedBackends = $config->getAppValue( - 'files_external', - 'user_mounting_backends', - '' - ); - $config->setAppValue( - 'files_external', - 'user_mounting_backends', - '\OC\Files\Storage\SMB' - ); } - public function tearDown() { - $config = \OC::$server->getConfig(); - $config->setAppValue( - 'files_external', - 'user_mounting_backends', - $this->oldAllowedBackends - ); - parent::tearDown(); - } + public function testAddOrUpdateStorageDisallowedBackend() { + $backend = $this->getBackendMock(); + $backend->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->willReturn(false); + $authMech = $this->getAuthMechMock(); - function disallowedBackendClassProvider() { - return array( - array('\OC\Files\Storage\Local'), - array('\OC\Files\Storage\FTP'), - ); - } - /** - * @dataProvider disallowedBackendClassProvider - */ - public function testAddOrUpdateStorageDisallowedBackend($backendClass) { + $storageConfig = new StorageConfig(1); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->exactly(2)) + ->method('createStorage') + ->will($this->returnValue($storageConfig)); $this->service->expects($this->never()) ->method('addStorage'); $this->service->expects($this->never()) @@ -86,7 +71,8 @@ public function testAddOrUpdateStorageDisallowedBackend($backendClass) { $response = $this->controller->create( 'mount', - $backendClass, + '\OC\Files\Storage\SMB', + '\Auth\Mechanism', array(), [], [], @@ -99,7 +85,8 @@ public function testAddOrUpdateStorageDisallowedBackend($backendClass) { $response = $this->controller->update( 1, 'mount', - $backendClass, + '\OC\Files\Storage\SMB', + '\Auth\Mechanism', array(), [], [], diff --git a/apps/files_external/tests/definitionparameterttest.php b/apps/files_external/tests/definitionparameterttest.php new file mode 100644 index 000000000000..6be00508698c --- /dev/null +++ b/apps/files_external/tests/definitionparameterttest.php @@ -0,0 +1,70 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +use \OCA\Files_External\Lib\DefinitionParameter as Param; + +class DefinitionParameterTest extends \Test\TestCase { + + public function testJsonSerialization() { + $param = new Param('foo', 'bar'); + $this->assertEquals('bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_BOOLEAN); + $this->assertEquals('!bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_PASSWORD); + $param->setFlag(Param::FLAG_OPTIONAL); + $this->assertEquals('&*bar', $param->jsonSerialize()); + + $param->setType(Param::VALUE_HIDDEN); + $param->setFlags(Param::FLAG_NONE); + $this->assertEquals('#bar', $param->jsonSerialize()); + } + + public function validateValueProvider() { + return [ + [Param::VALUE_TEXT, Param::FLAG_NONE, 'abc', true], + [Param::VALUE_TEXT, Param::FLAG_NONE, '', false], + [Param::VALUE_TEXT, Param::FLAG_OPTIONAL, '', true], + + [Param::VALUE_BOOLEAN, Param::FLAG_NONE, false, true], + [Param::VALUE_BOOLEAN, Param::FLAG_NONE, 123, false], + + [Param::VALUE_PASSWORD, Param::FLAG_NONE, 'foobar', true], + [Param::VALUE_PASSWORD, Param::FLAG_NONE, '', false], + + [Param::VALUE_HIDDEN, Param::FLAG_NONE, '', false] + ]; + } + + /** + * @dataProvider validateValueProvider + */ + public function testValidateValue($type, $flags, $value, $success) { + $param = new Param('foo', 'bar'); + $param->setType($type); + $param->setFlags($flags); + + $this->assertEquals($success, $param->validateValue($value)); + } +} diff --git a/apps/files_external/tests/dependencytraittest.php b/apps/files_external/tests/dependencytraittest.php new file mode 100644 index 000000000000..5706d97053d3 --- /dev/null +++ b/apps/files_external/tests/dependencytraittest.php @@ -0,0 +1,45 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +use \OCA\Files_External\Lib\MissingDependency; + +class DependencyTraitTest extends \Test\TestCase { + + public function testCheckDependencies() { + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\DependencyTrait'); + $trait->setDependencyCheck(function() { + return [ + (new MissingDependency('dependency'))->setMessage('missing dependency'), + (new MissingDependency('program'))->setMessage('cannot find program'), + ]; + }); + + $dependencies = $trait->checkDependencies(); + $this->assertCount(2, $dependencies); + $this->assertEquals('dependency', $dependencies[0]->getDependency()); + $this->assertEquals('missing dependency', $dependencies[0]->getMessage()); + $this->assertEquals('program', $dependencies[1]->getDependency()); + $this->assertEquals('cannot find program', $dependencies[1]->getMessage()); + } + +} diff --git a/apps/files_external/tests/dynamicmountconfig.php b/apps/files_external/tests/dynamicmountconfig.php deleted file mode 100644 index 48791ca89a59..000000000000 --- a/apps/files_external/tests/dynamicmountconfig.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @author Morris Jobke - * @author Thomas Müller - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -require_once __DIR__ . '/../../../lib/base.php'; - -/** - * Class Test_Mount_Config_Dummy_Backend - */ -class Test_Mount_Config_Dummy_Backend { - public static $checkDependencies = true; - - public static function checkDependencies() { - return self::$checkDependencies; - } -} - -/** - * Class Test_Dynamic_Mount_Config - */ -class Test_Dynamic_Mount_Config extends \Test\TestCase { - - private $backup; - - public function testRegistration() { - - // second registration shall return false - $result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array( - 'backend' => 'Test Dummy', - 'configuration' => array(), - 'has_dependencies' => true)); - - $this->assertTrue($result); - } - - public function testDependencyGetBackend() { - - // is the backend listed? - Test_Mount_Config_Dummy_Backend::$checkDependencies = true; - $backEnds = OC_Mount_Config::getBackends(); - $this->assertArrayHasKey('Test_Mount_Config_Dummy_Backend', $backEnds); - - // backend shall not be listed - Test_Mount_Config_Dummy_Backend::$checkDependencies = false; - - $backEnds = OC_Mount_Config::getBackends(); - $this->assertArrayNotHasKey('Test_Mount_Config_Dummy_Backend', $backEnds); - - } - - public function testCheckDependencies() { - - Test_Mount_Config_Dummy_Backend::$checkDependencies = true; - $message = OC_Mount_Config::checkDependencies(); - $this->assertEmpty($message); - - // backend shall not be listed - Test_Mount_Config_Dummy_Backend::$checkDependencies = array('dummy'); - - $message = OC_Mount_Config::checkDependencies(); - $this->assertEquals('
Note: "dummy" is not installed. Mounting of Test Dummy is not possible. Please ask your system administrator to install it.', - $message); - - } - - protected function setUp() { - parent::setUp(); - - $this->backup = OC_Mount_Config::setUp(); - - // register dummy backend - $result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array( - 'backend' => 'Test Dummy', - 'configuration' => array(), - 'has_dependencies' => true)); - - $this->assertTrue($result); - } - - protected function tearDown() - { - OC_Mount_Config::setUp($this->backup); - parent::tearDown(); - } -} diff --git a/apps/files_external/tests/frontenddefinitiontraittest.php b/apps/files_external/tests/frontenddefinitiontraittest.php new file mode 100644 index 000000000000..871a87d4c522 --- /dev/null +++ b/apps/files_external/tests/frontenddefinitiontraittest.php @@ -0,0 +1,83 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Files_External\Tests; + +class FrontendDefinitionTraitTest extends \Test\TestCase { + + public function testJsonSerialization() { + $param = $this->getMockBuilder('\OCA\Files_External\Lib\DefinitionParameter') + ->disableOriginalConstructor() + ->getMock(); + $param->method('getName')->willReturn('foo'); + + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\FrontendDefinitionTrait'); + $trait->setText('test'); + $trait->addParameters([$param]); + $trait->setCustomJs('foo/bar.js'); + + $json = $trait->jsonSerializeDefinition(); + + $this->assertEquals('test', $json['name']); + $this->assertEquals('foo/bar.js', $json['custom']); + + $configuration = $json['configuration']; + $this->assertArrayHasKey('foo', $configuration); + } + + public function validateStorageProvider() { + return [ + [true, ['foo' => true, 'bar' => true, 'baz' => true]], + [false, ['foo' => true, 'bar' => false]] + ]; + } + + /** + * @dataProvider validateStorageProvider + */ + public function testValidateStorage($expectedSuccess, $params) { + $backendParams = []; + foreach ($params as $name => $valid) { + $param = $this->getMockBuilder('\OCA\Files_External\Lib\DefinitionParameter') + ->disableOriginalConstructor() + ->getMock(); + $param->method('getName') + ->willReturn($name); + $param->expects($this->once()) + ->method('validateValue') + ->willReturn($valid); + $backendParams[] = $param; + } + + $storageConfig = $this->getMockBuilder('\OCA\Files_External\Lib\StorageConfig') + ->disableOriginalConstructor() + ->getMock(); + $storageConfig->expects($this->once()) + ->method('getBackendOptions') + ->willReturn([]); + + $trait = $this->getMockForTrait('\OCA\Files_External\Lib\FrontendDefinitionTrait'); + $trait->setText('test'); + $trait->addParameters($backendParams); + + $this->assertEquals($expectedSuccess, $trait->validateStorageDefinition($storageConfig)); + } +} diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 7cb86d7270b0..67a81277124c 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -39,6 +39,7 @@ describe('OCA.External.Settings tests', function() { '' + '' + '' + + '' + '' + '' + '' + @@ -58,6 +59,9 @@ describe('OCA.External.Settings tests', function() { 'field1': 'Display Name 1', 'field2': '&Display Name 2' }, + 'authSchemes': { + 'builtin': true, + }, 'priority': 11 }, '\\OC\\AnotherTestBackend': { @@ -66,10 +70,23 @@ describe('OCA.External.Settings tests', function() { 'field1': 'Display Name 1', 'field2': '&Display Name 2' }, + 'authSchemes': { + 'builtin': true, + }, 'priority': 12 } } ); + + $('#externalStorage #addMountPoint .authentication:first').data('mechanisms', { + 'mechanism1': { + 'name': 'Mechanism 1', + 'configuration': { + }, + 'scheme': 'builtin', + }, + }); + }); afterEach(function() { select2Stub.restore(); @@ -80,7 +97,7 @@ describe('OCA.External.Settings tests', function() { var view; function selectBackend(backendName) { - view.$el.find('.selectBackend:first').val('\\OC\\TestBackend').trigger('change'); + view.$el.find('.selectBackend:first').val(backendName).trigger('change'); } beforeEach(function() { @@ -139,7 +156,8 @@ describe('OCA.External.Settings tests', function() { var request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_external/globalstorages'); expect(JSON.parse(request.requestBody)).toEqual({ - backendClass: '\\OC\\TestBackend', + backend: '\\OC\\TestBackend', + authMechanism: 'mechanism1', backendOptions: { 'field1': 'test', 'field2': '' diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php deleted file mode 100644 index b76ba0a39a69..000000000000 --- a/apps/files_external/tests/mountconfig.php +++ /dev/null @@ -1,1157 +0,0 @@ - - * @author Joas Schilling - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -class Test_Mount_Config_Dummy_Storage extends \OC\Files\Storage\Common { - public function __construct($params) { - if (isset($params['simulateFail']) && $params['simulateFail'] == true) { - throw new \Exception('Simulated config validation fail'); - } - } - - public function getId() { - return 'dummy_storage'; - } - - public function mkdir($path) { - return false; - } - - public function rmdir($path) { - return false; - } - - public function opendir($path) { - return false; - } - - public function filetype($path) { - return false; - } - - public function file_exists($path) { - return false; - } - - public function unlink($path) { - return false; - } - - public function fopen($path, $mode) { - return false; - } - - public function touch($path, $mtime = null) { - return false; - } - - public function stat($path) { - return false; - } - - public function test() { - return true; - } -} - -class Test_Mount_Config_Storage_No_Personal extends Test_Mount_Config_Dummy_Storage { -} - -class Test_Mount_Config_Hook_Test { - static $signal; - static $params; - - public static function setUpHooks() { - self::clear(); - \OCP\Util::connectHook( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_create_mount, - '\Test_Mount_Config_Hook_Test', 'createHookCallback'); - \OCP\Util::connectHook( - \OC\Files\Filesystem::CLASSNAME, - \OC\Files\Filesystem::signal_delete_mount, - '\Test_Mount_Config_Hook_Test', 'deleteHookCallback'); - } - - public static function clear() { - self::$signal = null; - self::$params = null; - } - - public static function createHookCallback($params) { - self::$signal = \OC\Files\Filesystem::signal_create_mount; - self::$params = $params; - } - - public static function deleteHookCallback($params) { - self::$signal = \OC\Files\Filesystem::signal_delete_mount; - self::$params = $params; - } - - public static function getLastCall() { - return array(self::$signal, self::$params); - } -} - -/** - * Class Test_Mount_Config - */ -class Test_Mount_Config extends \Test\TestCase { - - private $dataDir; - private $userHome; - private $oldAllowedBackends; - - const TEST_USER1 = 'user1'; - const TEST_USER2 = 'user2'; - const TEST_GROUP1 = 'group1'; - const TEST_GROUP1B = 'group1b'; - const TEST_GROUP2 = 'group2'; - const TEST_GROUP2B = 'group2b'; - - protected function setUp() { - parent::setUp(); - - OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Storage', array( - 'backend' => 'dummy', - 'priority' => 150, - 'configuration' => array() - ) - ); - OC_Mount_Config::registerBackend('Test_Mount_Config_Storage_No_Personal', array( - 'backend' => 'dummy no personal', - 'priority' => 150, - 'configuration' => array() - ) - ); - - \OC_User::createUser(self::TEST_USER1, self::TEST_USER1); - \OC_User::createUser(self::TEST_USER2, self::TEST_USER2); - - \OC_Group::createGroup(self::TEST_GROUP1); - \OC_Group::createGroup(self::TEST_GROUP1B); - \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1); - \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1B); - \OC_Group::createGroup(self::TEST_GROUP2); - \OC_Group::createGroup(self::TEST_GROUP2B); - \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2); - \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2B); - - \OC_User::setUserId(self::TEST_USER1); - $this->userHome = \OC_User::getHome(self::TEST_USER1); - @mkdir($this->userHome); - - $this->dataDir = \OC_Config::getValue( - 'datadirectory', - \OC::$SERVERROOT . '/data/' - ); - $this->oldAllowedBackends = OCP\Config::getAppValue( - 'files_external', - 'user_mounting_backends', - '' - ); - OCP\Config::setAppValue( - 'files_external', - 'user_mounting_backends', - 'Test_Mount_Config_Dummy_Storage' - ); - - OC_Mount_Config::$skipTest = true; - Test_Mount_Config_Hook_Test::setupHooks(); - } - - protected function tearDown() { - Test_Mount_Config_Hook_Test::clear(); - OC_Mount_Config::$skipTest = false; - - \OC_User::deleteUser(self::TEST_USER2); - \OC_User::deleteUser(self::TEST_USER1); - \OC_Group::deleteGroup(self::TEST_GROUP1); - \OC_Group::deleteGroup(self::TEST_GROUP1B); - \OC_Group::deleteGroup(self::TEST_GROUP2); - \OC_Group::deleteGroup(self::TEST_GROUP2B); - - @unlink($this->dataDir . '/mount.json'); - - OCP\Config::setAppValue( - 'files_external', - 'user_mounting_backends', - $this->oldAllowedBackends - ); - - parent::tearDown(); - } - - /** - * Reads the global config, for checking - */ - private function readGlobalConfig() { - $configFile = $this->dataDir . '/mount.json'; - return json_decode(file_get_contents($configFile), true); - } - - private function writeGlobalConfig($config) { - $configFile = $this->dataDir . '/mount.json'; - file_put_contents($configFile, json_encode($config)); - } - - /** - * Reads the user config, for checking - */ - private function readUserConfig() { - $configFile = $this->userHome . '/mount.json'; - return json_decode(file_get_contents($configFile), true); - } - - /** - * Write the user config, to simulate existing files - */ - private function writeUserConfig($config) { - $configFile = $this->userHome . '/mount.json'; - file_put_contents($configFile, json_encode($config)); - } - - /** - * Test mount point validation - */ - public function testAddMountPointValidation() { - $storageClass = 'Test_Mount_Config_Dummy_Storage'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - $this->assertFalse(OC_Mount_Config::addMountPoint('', $storageClass, array(), $mountType, $applicable, $isPersonal)); - $this->assertFalse(OC_Mount_Config::addMountPoint('/', $storageClass, array(), $mountType, $applicable, $isPersonal)); - } - - /** - * Test adding a global mount point - */ - public function testAddGlobalMountPoint() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = 'all'; - $isPersonal = false; - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - $this->assertEquals(0, OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Dummy_Storage', $storageOptions, $mountType, $applicable, $isPersonal)); - - $config = $this->readGlobalConfig(); - $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config[$mountType])); - $this->assertTrue(isset($config[$mountType][$applicable])); - $this->assertTrue(isset($config[$mountType][$applicable]['/$user/files/ext'])); - $this->assertEquals( - 'Test_Mount_Config_Dummy_Storage', - $config[$mountType][$applicable]['/$user/files/ext']['class'] - ); - } - - /** - * Test adding a personal mount point - */ - public function testAddMountPointSingleUser() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - $this->assertEquals(0, OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Dummy_Storage', $storageOptions, $mountType, $applicable, $isPersonal)); - - $config = $this->readUserConfig(); - $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config[$mountType])); - $this->assertTrue(isset($config[$mountType][$applicable])); - $this->assertTrue(isset($config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext'])); - $this->assertEquals( - 'Test_Mount_Config_Dummy_Storage', - $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['class'] - ); - } - - /** - * Test adding a personal mount point using disallowed backend - */ - public function testAddDisallowedBackendMountPointSingleUser() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - - // local - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', '\OC\Files\Storage\Local', array(), $mountType, $applicable, $isPersonal)); - - $storageOptions = array( - 'host' => 'localhost', - 'user' => 'testuser', - 'password' => '12345', - ); - - // non-local but forbidden - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', 'Test_Mount_Config_Storage_No_Personal', $storageOptions, $mountType, $applicable, $isPersonal)); - - $this->assertFalse(file_exists($this->userHome . '/mount.json')); - } - - /** - * Test adding a mount point with an non-existant backend - */ - public function testAddMountPointUnexistClass() { - $storageClass = 'Unexist_Storage'; - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = false; - $this->assertFalse(OC_Mount_Config::addMountPoint('/ext', $storageClass, array(), $mountType, $applicable, $isPersonal)); - - } - - /** - * Provider for testing configurations with different - * "applicable" values (all, user, groups) - */ - public function applicableConfigProvider() { - return array( - // applicable to "all" - array( - OC_Mount_Config::MOUNT_TYPE_USER, - 'all', - array( - 'users' => array('all'), - 'groups' => array() - ) - ), - // applicable to single user - array( - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - array( - 'users' => array(self::TEST_USER1), - 'groups' => array() - ) - ), - // applicable to single group - array( - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - array( - 'users' => array(), - 'groups' => array(self::TEST_GROUP1) - ) - ), - ); - } - - /** - * Test reading and writing global config - * - * @dataProvider applicableConfigProvider - */ - public function testReadWriteGlobalConfig($mountType, $applicable, $expectApplicableArray) { - - $mountType = $mountType; - $applicable = $applicable; - $isPersonal = false; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - $mountType, - $applicable, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($expectApplicableArray, $config[0]['applicable']); - $savedOptions = $config[0]['options']; - $this->assertEquals($options, $savedOptions); - // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($options), array_keys($savedOptions)); - } - - /** - * Test reading and writing config - */ - public function testReadWritePersonalConfig() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - $mountType, - $applicable, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getPersonalMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $savedOptions = $config[0]['options']; - $this->assertEquals($options, $savedOptions); - // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($options), array_keys($savedOptions)); - } - - public function testHooks() { - $mountPoint = '/test'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - $mountPoint, - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - \OC\Files\Filesystem::signal_create_mount, - $hookName - ); - $this->assertEquals( - $mountPoint, - $params[\OC\Files\Filesystem::signal_param_path] - ); - $this->assertEquals( - $mountType, - $params[\OC\Files\Filesystem::signal_param_mount_type] - ); - $this->assertEquals( - $applicable, - $params[\OC\Files\Filesystem::signal_param_users] - ); - - Test_Mount_Config_Hook_Test::clear(); - - // edit - $mountConfig['host'] = 'anothersmbhost'; - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - $mountPoint, - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // hook must not be called on edit - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - null, - $hookName - ); - - Test_Mount_Config_Hook_Test::clear(); - - $this->assertTrue( - OC_Mount_Config::removeMountPoint( - $mountPoint, - $mountType, - $applicable, - $isPersonal - ) - ); - - list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall(); - $this->assertEquals( - \OC\Files\Filesystem::signal_delete_mount, - $hookName - ); - $this->assertEquals( - $mountPoint, - $params[\OC\Files\Filesystem::signal_param_path] - ); - $this->assertEquals( - $mountType, - $params[\OC\Files\Filesystem::signal_param_mount_type] - ); - $this->assertEquals( - $applicable, - $params[\OC\Files\Filesystem::signal_param_users] - ); - } - - /** - * Test password obfuscation - */ - public function testPasswordObfuscation() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // note: password re-reading is covered by testReadWritePersonalConfig - - // check that password inside the file is NOT in plain text - $config = $this->readUserConfig(); - $savedConfig = $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options']; - - // no more clear text password in file (kept because of key order) - $this->assertEquals('', $savedConfig['password']); - - // encrypted password is present - $this->assertNotEquals($mountConfig['password'], $savedConfig['password_encrypted']); - } - - /** - * Test read legacy passwords - */ - public function testReadLegacyPassword() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = self::TEST_USER1; - $isPersonal = true; - $mountConfig = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - $config = $this->readUserConfig(); - // simulate non-encrypted password situation - $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options']['password'] = 'smbpasswd'; - - $this->writeUserConfig($config); - - // re-read config, password was read correctly - $config = OC_Mount_Config::getPersonalMountPoints(); - $savedMountConfig = $config[0]['options']; - $this->assertEquals($mountConfig, $savedMountConfig); - } - - public function testVariableSubstitution() { - $legacyBackendOptions = [ - 'user' => 'someuser', - 'password' => 'somepassword', - 'replacethis' => '$user', - ]; - $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); - - $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', - 'options' => $legacyBackendOptions, - 'mountOptions' => ['preview' => false, 'int' => 1], - ]; - // different mount options - $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', - 'options' => $legacyBackendOptions, - 'mountOptions' => ['preview' => true, 'string' => 'abc'], - ]; - - $json = [ - 'user' => [ - self::TEST_USER1 => [ - '/$user/files/somemount' => $legacyConfig, - '/$user/files/anothermount' => $legacyConfig2, - ], - ], - ]; - - $this->writeGlobalConfig($json); - - // re-read config, password was read correctly - $config = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $config1 = $config['/' . self::TEST_USER1 . '/files/somemount']; - $config2 = $config['/' . self::TEST_USER1 . '/files/anothermount']; - - $this->assertSame(self::TEST_USER1, $config1['options']['replacethis']); - $this->assertSame(self::TEST_USER1, $config1['options']['replacethis']); - $this->assertSame(1, $config1['mountOptions']['int']); - $this->assertSame(true, $config2['mountOptions']['preview']); - $this->assertSame('abc', $config2['mountOptions']['string']); - } - - - public function mountDataProvider() { - return array( - // Tests for visible mount points - // system mount point for all users - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - 'all', - self::TEST_USER1, - true, - ), - // system mount point for a specific user - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER1, - true, - ), - // system mount point for a specific group - array( - false, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - self::TEST_USER1, - true, - ), - // user mount point - array( - true, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER1, - true, - ), - - // Tests for non-visible mount points - // system mount point for another user - array( - false, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER2, - self::TEST_USER1, - false, - ), - // system mount point for a specific group - array( - false, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP2, - self::TEST_USER1, - false, - ), - // user mount point - array( - true, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - self::TEST_USER2, - false, - ), - ); - } - - /** - * Test mount points used at mount time, making sure - * the configuration is prepared properly. - * - * @dataProvider mountDataProvider - * @param bool $isPersonal true for personal mount point, false for system mount point - * @param string $mountType mount type - * @param string $applicable target user/group or "all" - * @param string $testUser user for which to retrieve the mount points - * @param bool $expectVisible whether to expect the mount point to be visible for $testUser - */ - public function testMount($isPersonal, $mountType, $applicable, $testUser, $expectVisible) { - - $mountConfig = array( - 'host' => 'someost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // add mount point as "test" user - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - $mountType, - $applicable, - $isPersonal - ) - ); - - // check mount points in the perspective of user $testUser - \OC_User::setUserId($testUser); - - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints($testUser); - if ($expectVisible) { - $this->assertEquals(1, count($mountPoints)); - $this->assertTrue(isset($mountPoints['/' . self::TEST_USER1 . '/files/ext'])); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $mountPoints['/' . self::TEST_USER1 . '/files/ext']['class']); - $this->assertEquals($mountConfig, $mountPoints['/' . self::TEST_USER1 . '/files/ext']['options']); - } - else { - $this->assertEquals(0, count($mountPoints)); - } - } - - /** - * Test the same config for multiple users. - * The config will be merged by getSystemMountPoints(). - */ - public function testConfigMerging() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $isPersonal = false; - $options = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER2, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP2, - $isPersonal - ) - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options, - OC_Mount_Config::MOUNT_TYPE_GROUP, - self::TEST_GROUP1, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($options, $config[0]['options']); - $this->assertEquals(array(self::TEST_USER1, self::TEST_USER2), $config[0]['applicable']['users']); - $this->assertEquals(array(self::TEST_GROUP2, self::TEST_GROUP1), $config[0]['applicable']['groups']); - } - - /** - * Create then re-read mount points configs where the mount points - * have the same path, the config must NOT be merged. - */ - public function testRereadMountpointWithSamePath() { - - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $isPersonal = false; - $options1 = array( - 'host' => 'smbhost', - 'user' => 'smbuser', - 'password' => 'smbpassword', - 'share' => 'smbshare', - 'root' => 'smbroot' - ); - - // write config - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options1, - $mountType, - self::TEST_USER1, - $isPersonal - ) - ); - - $options2 = array( - 'host' => 'anothersmbhost', - 'user' => 'anothersmbuser', - 'password' => 'anothersmbpassword', - 'share' => 'anothersmbshare', - 'root' => 'anothersmbroot' - ); - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $options2, - $mountType, - self::TEST_USER2, - $isPersonal - ) - ); - - // re-read config - $config = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(2, count($config)); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[0]['class']); - $this->assertEquals('ext', $config[0]['mountpoint']); - $this->assertEquals($options1, $config[0]['options']); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', $config[1]['class']); - $this->assertEquals('ext', $config[1]['mountpoint']); - $this->assertEquals($options2, $config[1]['options']); - } - - public function priorityDataProvider() { - return array( - - // test 1 - group vs group - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1, - 'priority' => 50 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1B, - 'priority' => 60 - ) - ), - 1 - ), - // test 2 - user vs personal - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => 2000 - ), - array( - 'isPersonal' => true, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => null - ) - ), - 1 - ), - // test 3 - all vs group vs user - array( - array( - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => 'all', - 'priority' => 70 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, - 'applicable' => self::TEST_GROUP1, - 'priority' => 60 - ), - array( - 'isPersonal' => false, - 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, - 'applicable' => self::TEST_USER1, - 'priority' => 50 - ) - ), - 2 - ) - - ); - } - - /** - * Ensure priorities are being respected - * Test user is self::TEST_USER1 - * - * @dataProvider priorityDataProvider - * @param array[] $mounts array of associative array of mount parameters: - * bool $isPersonal - * string $mountType - * string $applicable - * int|null $priority null for personal - * @param int $expected index of expected visible mount - */ - public function testPriority($mounts, $expected) { - - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // Add mount points - foreach($mounts as $i => $mount) { - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig + array('id' => $i), - $mount['mountType'], - $mount['applicable'], - $mount['isPersonal'], - $mount['priority'] - ) - ); - } - - // Get mount points for user - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $this->assertEquals(1, count($mountPoints)); - $this->assertEquals($expected, $mountPoints['/'.self::TEST_USER1.'/files/ext']['options']['id']); - } - - /** - * Test for persistence of priority when changing mount options - */ - public function testPriorityPersistence() { - - $class = 'Test_Mount_Config_Dummy_Storage'; - $priority = 123; - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $class, - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - false, - $priority - ) - ); - - // Check for correct priority - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - $this->assertEquals($priority, - $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); - - // Simulate changed mount options (without priority set) - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $class, - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - false - ) - ); - - // Check for correct priority - $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - $this->assertEquals($priority, - $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); - } - - /* - * Test for correct personal configuration loading in file sharing scenarios - */ - public function testMultiUserPersonalConfigLoading() { - $mountConfig = array( - 'host' => 'somehost', - 'user' => 'someuser', - 'password' => 'somepassword', - 'root' => 'someroot', - 'share' => '', - ); - - // Create personal mount point - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - 'Test_Mount_Config_Dummy_Storage', - $mountConfig, - OC_Mount_Config::MOUNT_TYPE_USER, - self::TEST_USER1, - true - ) - ); - - // Ensure other user can read mount points - \OC_User::setUserId(self::TEST_USER2); - $mountPointsMe = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER2); - $mountPointsOther = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); - - $this->assertEquals(0, count($mountPointsMe)); - $this->assertEquals(1, count($mountPointsOther)); - $this->assertTrue(isset($mountPointsOther['/'.self::TEST_USER1.'/files/ext'])); - $this->assertEquals('Test_Mount_Config_Dummy_Storage', - $mountPointsOther['/'.self::TEST_USER1.'/files/ext']['class']); - $this->assertEquals($mountConfig, - $mountPointsOther['/'.self::TEST_USER1.'/files/ext']['options']); - } - - public function testAllowWritingIncompleteConfigIfStorageContructorFails() { - $storageClass = 'Test_Mount_Config_Dummy_Storage'; - $mountType = 'user'; - $applicable = 'all'; - $isPersonal = false; - - $this->assertEquals( - 0, - OC_Mount_Config::addMountPoint( - '/ext', - $storageClass, - array('simulateFail' => true), - $mountType, - $applicable, - $isPersonal - ) - ); - - // config can be retrieved afterwards - $mounts = OC_Mount_Config::getSystemMountPoints(); - $this->assertEquals(1, count($mounts)); - - // no storage id was set - $this->assertFalse(isset($mounts[0]['storage_id'])); - } -} diff --git a/apps/files_external/tests/service/backendservicetest.php b/apps/files_external/tests/service/backendservicetest.php new file mode 100644 index 000000000000..08f6b9bf988c --- /dev/null +++ b/apps/files_external/tests/service/backendservicetest.php @@ -0,0 +1,152 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCA\Files_External\Tests\Service; + +use \OCA\Files_External\Service\BackendService; + +class BackendServiceTest extends \Test\TestCase { + + /** @var \OCP\IConfig */ + protected $config; + + /** @var \OCP\IL10N */ + protected $l10n; + + protected function setUp() { + $this->config = $this->getMock('\OCP\IConfig'); + $this->l10n = $this->getMock('\OCP\IL10N'); + } + + protected function getBackendMock($class) { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getIdentifier')->will($this->returnValue('identifier:'.$class)); + $backend->method('getIdentifierAliases')->will($this->returnValue(['identifier:'.$class])); + return $backend; + } + + public function testRegisterBackend() { + $service = new BackendService($this->config, $this->l10n); + + $backend = $this->getBackendMock('\Foo\Bar'); + + $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backendAlias->method('getIdentifierAliases') + ->willReturn(['identifier_real', 'identifier_alias']); + $backendAlias->method('getIdentifier') + ->willReturn('identifier_real'); + + $service->registerBackend($backend); + $service->registerBackend($backendAlias); + + $this->assertEquals($backend, $service->getBackend('identifier:\Foo\Bar')); + $this->assertEquals($backendAlias, $service->getBackend('identifier_real')); + $this->assertEquals($backendAlias, $service->getBackend('identifier_alias')); + + $backends = $service->getBackends(); + $this->assertCount(2, $backends); + $this->assertArrayHasKey('identifier:\Foo\Bar', $backends); + $this->assertArrayHasKey('identifier_real', $backends); + $this->assertArrayNotHasKey('identifier_alias', $backends); + } + + public function testUserMountingBackends() { + $this->config->expects($this->exactly(2)) + ->method('getAppValue') + ->will($this->returnValueMap([ + ['files_external', 'allow_user_mounting', 'yes', 'yes'], + ['files_external', 'user_mounting_backends', '', 'identifier:\User\Mount\Allowed,identifier_alias'] + ])); + + $service = new BackendService($this->config, $this->l10n); + + $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); + $backendAllowed->expects($this->never()) + ->method('removeVisibility'); + $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); + $backendNotAllowed->expects($this->once()) + ->method('removeVisibility') + ->with(BackendService::VISIBILITY_PERSONAL); + + $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backendAlias->method('getIdentifierAliases') + ->willReturn(['identifier_real', 'identifier_alias']); + $backendAlias->expects($this->never()) + ->method('removeVisibility'); + + $service->registerBackend($backendAllowed); + $service->registerBackend($backendNotAllowed); + $service->registerBackend($backendAlias); + } + + public function testGetAvailableBackends() { + $service = new BackendService($this->config, $this->l10n); + + $backendAvailable = $this->getBackendMock('\Backend\Available'); + $backendAvailable->expects($this->once()) + ->method('checkDependencies') + ->will($this->returnValue([])); + $backendNotAvailable = $this->getBackendMock('\Backend\NotAvailable'); + $backendNotAvailable->expects($this->once()) + ->method('checkDependencies') + ->will($this->returnValue([ + $this->getMockBuilder('\OCA\Files_External\Lib\MissingDependency') + ->disableOriginalConstructor() + ->getMock() + ])); + + $service->registerBackend($backendAvailable); + $service->registerBackend($backendNotAvailable); + + $availableBackends = $service->getAvailableBackends(); + $this->assertArrayHasKey('identifier:\Backend\Available', $availableBackends); + $this->assertArrayNotHasKey('identifier:\Backend\NotAvailable', $availableBackends); + } + + public function testGetUserBackends() { + $service = new BackendService($this->config, $this->l10n); + + $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); + $backendAllowed->expects($this->once()) + ->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->will($this->returnValue(true)); + $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); + $backendNotAllowed->expects($this->once()) + ->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) + ->will($this->returnValue(false)); + + $service->registerBackend($backendAllowed); + $service->registerBackend($backendNotAllowed); + + $userBackends = $service->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL); + $this->assertArrayHasKey('identifier:\User\Mount\Allowed', $userBackends); + $this->assertArrayNotHasKey('identifier:\User\Mount\NotAllowed', $userBackends); + } + +} + diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index ac25f58b1d1d..05585d2c065e 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -29,7 +29,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest { public function setUp() { parent::setUp(); - $this->service = new GlobalStoragesService(); + $this->service = new GlobalStoragesService($this->backendService); } public function tearDown() { @@ -40,7 +40,8 @@ public function tearDown() { protected function makeTestStorageData() { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -59,9 +60,10 @@ function storageDataProvider() { return [ // all users [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -70,13 +72,14 @@ function storageDataProvider() { 'applicableUsers' => [], 'applicableGroups' => [], 'priority' => 15, - ]), + ], ], // some users [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -85,13 +88,14 @@ function storageDataProvider() { 'applicableUsers' => ['user1', 'user2'], 'applicableGroups' => [], 'priority' => 15, - ]), + ], ], // some groups [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -100,13 +104,14 @@ function storageDataProvider() { 'applicableUsers' => [], 'applicableGroups' => ['group1', 'group2'], 'priority' => 15, - ]), + ], ], // both users and groups [ - $this->makeStorageConfig([ + [ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -115,7 +120,7 @@ function storageDataProvider() { 'applicableUsers' => ['user1', 'user2'], 'applicableGroups' => ['group1', 'group2'], 'priority' => 15, - ]), + ], ], ]; } @@ -123,7 +128,8 @@ function storageDataProvider() { /** * @dataProvider storageDataProvider */ - public function testAddStorage($storage) { + public function testAddStorage($storageParams) { + $storage = $this->makeStorageConfig($storageParams); $newStorage = $this->service->addStorage($storage); $this->assertEquals(1, $newStorage->getId()); @@ -132,7 +138,8 @@ public function testAddStorage($storage) { $newStorage = $this->service->getStorage(1); $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); - $this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass()); + $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); + $this->assertEquals($storage->getAuthMechanism(), $newStorage->getAuthMechanism()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals($storage->getApplicableUsers(), $newStorage->getApplicableUsers()); $this->assertEquals($storage->getApplicableGroups(), $newStorage->getApplicableGroups()); @@ -148,10 +155,12 @@ public function testAddStorage($storage) { /** * @dataProvider storageDataProvider */ - public function testUpdateStorage($updatedStorage) { + public function testUpdateStorage($updatedStorageParams) { + $updatedStorage = $this->makeStorageConfig($updatedStorageParams); $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -638,7 +647,8 @@ public function testLegacyConfigConversionApplicableAll() { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -678,7 +688,8 @@ public function testLegacyConfigConversionApplicableUserAndGroup() { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -695,7 +706,8 @@ public function testLegacyConfigConversionApplicableUserAndGroup() { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(15, $mountPointOptions['priority']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); @@ -720,13 +732,15 @@ public function testReadLegacyConfigAndGenerateConfigId() { $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; @@ -737,7 +751,8 @@ public function testReadLegacyConfigAndGenerateConfigId() { // different config $legacyConfig3 = [ - 'class' => '\OC\Files\Storage\SMB', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions2, 'mountOptions' => ['preview' => true], ]; @@ -811,4 +826,85 @@ public function testReadLegacyConfigAndGenerateConfigId() { $this->assertEquals([], $storage4->getApplicableGroups()); $this->assertEquals(['preview' => true], $storage4->getMountOptions()); } + + public function testReadLegacyConfigNoAuthMechanism() { + $configFile = $this->dataDir . '/mount.json'; + + $json = [ + 'user' => [ + 'user1' => [ + '/$user/files/somemount' => [ + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + '/$user/files/othermount' => [ + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + // no authMechanism + 'options' => [], + 'mountOptions' => [], + ], + ] + ] + ]; + + file_put_contents($configFile, json_encode($json)); + + $allStorages = $this->service->getAllStorages(); + + $this->assertCount(2, $allStorages); + + $storage1 = $allStorages[1]; + $storage2 = $allStorages[2]; + + $this->assertEquals('/somemount', $storage1->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage1->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage1->getAuthMechanism()->getIdentifier()); + + $this->assertEquals('/othermount', $storage2->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage2->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Other\Auth\Mechanism', $storage2->getAuthMechanism()->getIdentifier()); + } + + public function testReadLegacyConfigClass() { + $configFile = $this->dataDir . '/mount.json'; + + $json = [ + 'user' => [ + 'user1' => [ + '/$user/files/somemount' => [ + 'class' => 'identifier:\OCA\Files_External\Lib\Backend\SFTP', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + '/$user/files/othermount' => [ + 'class' => 'identifier:sftp_alias', + 'authMechanism' => 'identifier:\Auth\Mechanism', + 'options' => [], + 'mountOptions' => [], + ], + ] + ] + ]; + + file_put_contents($configFile, json_encode($json)); + + $allStorages = $this->service->getAllStorages(); + + $this->assertCount(2, $allStorages); + + $storage1 = $allStorages[1]; + $storage2 = $allStorages[2]; + + $this->assertEquals('/somemount', $storage1->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage1->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage1->getAuthMechanism()->getIdentifier()); + + $this->assertEquals('/othermount', $storage2->getMountPoint()); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SFTP', $storage2->getBackend()->getIdentifier()); + $this->assertEquals('identifier:\Auth\Mechanism', $storage2->getAuthMechanism()->getIdentifier()); + } + } diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 36f68a83b111..07286106c7bb 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -1,6 +1,7 @@ + * @author Robin McCorkell * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -24,6 +25,7 @@ use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\BackendService; abstract class StoragesServiceTest extends \Test\TestCase { @@ -32,6 +34,9 @@ abstract class StoragesServiceTest extends \Test\TestCase { */ protected $service; + /** @var BackendService */ + protected $backendService; + /** * Data directory * @@ -55,6 +60,51 @@ public function setUp() { ); \OC_Mount_Config::$skipTest = true; + // prepare BackendService mock + $this->backendService = + $this->getMockBuilder('\OCA\Files_External\Service\BackendService') + ->disableOriginalConstructor() + ->getMock(); + + $authMechanisms = [ + 'identifier:\Auth\Mechanism' => $this->getAuthMechMock('null', '\Auth\Mechanism'), + 'identifier:\Other\Auth\Mechanism' => $this->getAuthMechMock('null', '\Other\Auth\Mechanism'), + 'identifier:\OCA\Files_External\Lib\Auth\NullMechanism' => $this->getAuthMechMock(), + ]; + $this->backendService->method('getAuthMechanism') + ->will($this->returnCallback(function($class) use ($authMechanisms) { + if (isset($authMechanisms[$class])) { + return $authMechanisms[$class]; + } + return null; + })); + $this->backendService->method('getAuthMechanismsByScheme') + ->will($this->returnCallback(function($schemes) use ($authMechanisms) { + return array_filter($authMechanisms, function ($authMech) use ($schemes) { + return in_array($authMech->getScheme(), $schemes, true); + }); + })); + $this->backendService->method('getAuthMechanisms') + ->will($this->returnValue($authMechanisms)); + + $sftpBackend = $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'); + $backends = [ + 'identifier:\OCA\Files_External\Lib\Backend\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'), + 'identifier:\OCA\Files_External\Lib\Backend\SFTP' => $sftpBackend, + 'identifier:sftp_alias' => $sftpBackend, + ]; + $backends['identifier:\OCA\Files_External\Lib\Backend\SFTP']->method('getLegacyAuthMechanism') + ->willReturn($authMechanisms['identifier:\Other\Auth\Mechanism']); + $this->backendService->method('getBackend') + ->will($this->returnCallback(function($backendClass) use ($backends) { + if (isset($backends[$backendClass])) { + return $backends[$backendClass]; + } + return null; + })); + $this->backendService->method('getBackends') + ->will($this->returnValue($backends)); + \OCP\Util::connectHook( Filesystem::CLASSNAME, Filesystem::signal_create_mount, @@ -64,6 +114,14 @@ public function setUp() { Filesystem::signal_delete_mount, get_class($this), 'deleteHookCallback'); + $containerMock = $this->getMock('\OCP\AppFramework\IAppContainer'); + $containerMock->method('query') + ->will($this->returnCallback(function($name) { + if ($name === 'OCA\Files_External\Service\BackendService') { + return $this->backendService; + } + })); + \OC_Mount_Config::initApp($containerMock); } public function tearDown() { @@ -71,6 +129,29 @@ public function tearDown() { self::$hookCalls = array(); } + protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getStorageClass') + ->willReturn($storageClass); + $backend->method('getIdentifier') + ->willReturn('identifier:'.$class); + return $backend; + } + + protected function getAuthMechMock($scheme = 'null', $class = '\OCA\Files_External\Lib\Auth\NullMechanism') { + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getScheme') + ->willReturn($scheme); + $authMech->method('getIdentifier') + ->willReturn('identifier:'.$class); + + return $authMech; + } + /** * Creates a StorageConfig instance based on array data * @@ -84,7 +165,22 @@ protected function makeStorageConfig($data) { $storage->setId($data['id']); } $storage->setMountPoint($data['mountPoint']); - $storage->setBackendClass($data['backendClass']); + if (!isset($data['backend'])) { + // data providers are run before $this->backendService is initialised + // so $data['backend'] can be specified directly + $data['backend'] = $this->backendService->getBackend($data['backendIdentifier']); + } + if (!isset($data['backend'])) { + throw new \Exception('oops, no backend'); + } + if (!isset($data['authMechanism'])) { + $data['authMechanism'] = $this->backendService->getAuthMechanism($data['authMechanismIdentifier']); + } + if (!isset($data['authMechanism'])) { + throw new \Exception('oops, no auth mechanism'); + } + $storage->setBackend($data['backend']); + $storage->setAuthMechanism($data['authMechanism']); $storage->setBackendOptions($data['backendOptions']); if (isset($data['applicableUsers'])) { $storage->setApplicableUsers($data['applicableUsers']); @@ -106,16 +202,22 @@ protected function makeStorageConfig($data) { * @expectedException \OCA\Files_external\NotFoundException */ public function testNonExistingStorage() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); - $storage->setBackendClass('\OC\Files\Storage\SMB'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); $this->service->updateStorage($storage); } public function testDeleteStorage() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); $storage = new StorageConfig(255); $storage->setMountPoint('mountpoint'); - $storage->setBackendClass('\OC\Files\Storage\SMB'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); $storage->setBackendOptions(['password' => 'testPassword']); $newStorage = $this->service->addStorage($storage); @@ -140,6 +242,64 @@ public function testDeleteUnexistingStorage() { $this->service->removeStorage(255); } + public function testCreateStorage() { + $mountPoint = 'mount'; + $backendIdentifier = 'identifier:\OCA\Files_External\Lib\Backend\SMB'; + $authMechanismIdentifier = 'identifier:\Auth\Mechanism'; + $backendOptions = ['param' => 'foo', 'param2' => 'bar']; + $mountOptions = ['option' => 'foobar']; + $applicableUsers = ['user1', 'user2']; + $applicableGroups = ['group']; + $priority = 123; + + $backend = $this->backendService->getBackend($backendIdentifier); + $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier); + + $storage = $this->service->createStorage( + $mountPoint, + $backendIdentifier, + $authMechanismIdentifier, + $backendOptions, + $mountOptions, + $applicableUsers, + $applicableGroups, + $priority + ); + + $this->assertEquals('/'.$mountPoint, $storage->getMountPoint()); + $this->assertEquals($backend, $storage->getBackend()); + $this->assertEquals($authMechanism, $storage->getAuthMechanism()); + $this->assertEquals($backendOptions, $storage->getBackendOptions()); + $this->assertEquals($mountOptions, $storage->getMountOptions()); + $this->assertEquals($applicableUsers, $storage->getApplicableUsers()); + $this->assertEquals($applicableGroups, $storage->getApplicableGroups()); + $this->assertEquals($priority, $storage->getPriority()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateStorageInvalidClass() { + $this->service->createStorage( + 'mount', + 'identifier:\OC\Not\A\Backend', + 'identifier:\Auth\Mechanism', + [] + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateStorageInvalidAuthMechanismClass() { + $this->service->createStorage( + 'mount', + 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'identifier:\Not\An\Auth\Mechanism', + [] + ); + } + public static function createHookCallback($params) { self::$hookCalls[] = array( 'signal' => Filesystem::signal_create_mount, diff --git a/apps/files_external/tests/service/userglobalstoragesservicetest.php b/apps/files_external/tests/service/userglobalstoragesservicetest.php new file mode 100644 index 000000000000..49a02453840f --- /dev/null +++ b/apps/files_external/tests/service/userglobalstoragesservicetest.php @@ -0,0 +1,215 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCA\Files_External\Tests\Service; + +use \OCA\Files_External\Service\UserGlobalStoragesService; +use \OCP\IGroupManager; + +use \OCA\Files_External\Lib\StorageConfig; + +class UserGlobalStoragesServiceTest extends GlobalStoragesServiceTest { + + protected $groupManager; + + protected $globalStoragesService; + + protected $user; + + const USER_ID = 'test_user'; + const GROUP_ID = 'test_group'; + + public function setUp() { + parent::setUp(); + + $this->globalStoragesService = $this->service; + + $this->user = new \OC\User\User(self::USER_ID, null); + $userSession = $this->getMock('\OCP\IUserSession'); + $userSession + ->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($this->user)); + + $this->groupManager = $this->getMock('\OCP\IGroupManager'); + $this->groupManager->method('isInGroup') + ->will($this->returnCallback(function($userId, $groupId) { + if ($userId === self::USER_ID && $groupId === self::GROUP_ID) { + return true; + } + return false; + })); + + $this->service = new UserGlobalStoragesService( + $this->backendService, + $userSession, + $this->groupManager + ); + } + + public function applicableStorageProvider() { + return [ + [[], [], true], + + // not applicable cases + [['user1'], [], false], + [[], ['group1'], false], + [['user1'], ['group1'], false], + + // applicable cases + [[self::USER_ID], [], true], + [[], [self::GROUP_ID], true], + [[self::USER_ID], ['group1'], true], + [['user1'], [self::GROUP_ID], true], + + // sanity checks + [['user1', 'user2', self::USER_ID, 'user3'], [], true], + ]; + } + + /** + * @dataProvider applicableStorageProvider + */ + public function testGetStorageWithApplicable($applicableUsers, $applicableGroups, $isVisible) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + $storage->setApplicableUsers($applicableUsers); + $storage->setApplicableGroups($applicableGroups); + + $newStorage = $this->globalStoragesService->addStorage($storage); + + $storages = $this->service->getAllStorages(); + if ($isVisible) { + $this->assertEquals(1, count($storages)); + $retrievedStorage = $this->service->getStorage($newStorage->getId()); + $this->assertEquals('/mountpoint', $retrievedStorage->getMountPoint()); + } else { + $this->assertEquals(0, count($storages)); + } + + } + + /** + * @expectedException \DomainException + */ + public function testAddStorage($storageParams = null) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $this->service->addStorage($storage); + } + + /** + * @expectedException \DomainException + */ + public function testUpdateStorage($storageParams = null) { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->globalStoragesService->addStorage($storage); + + $retrievedStorage = $this->service->getStorage($newStorage->getId()); + $retrievedStorage->setMountPoint('abc'); + $this->service->updateStorage($retrievedStorage); + } + + /** + * @expectedException \DomainException + */ + public function testDeleteStorage() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->globalStoragesService->addStorage($storage); + $this->assertEquals(1, $newStorage->getId()); + + $this->service->removeStorage(1); + } + + public function testHooksAddStorage($a = null, $b = null, $c = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksUpdateStorage($a = null, $b = null, $c = null, $d = null, $e = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksRenameMountPoint() { + // we don't test this here + $this->assertTrue(true); + } + + public function testHooksDeleteStorage($a = null, $b = null, $c = null) { + // we don't test this here + $this->assertTrue(true); + } + + public function testLegacyConfigConversionApplicableAll() { + // we don't test this here + $this->assertTrue(true); + } + + public function testLegacyConfigConversionApplicableUserAndGroup() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigAndGenerateConfigId() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigNoAuthMechanism() { + // we don't test this here + $this->assertTrue(true); + } + + public function testReadLegacyConfigClass() { + // we don't test this here + $this->assertTrue(true); + } + +} diff --git a/apps/files_external/tests/service/userstoragesservicetest.php b/apps/files_external/tests/service/userstoragesservicetest.php index ab102741ee2c..0d5b82e2f8c8 100644 --- a/apps/files_external/tests/service/userstoragesservicetest.php +++ b/apps/files_external/tests/service/userstoragesservicetest.php @@ -40,7 +40,7 @@ public function setUp() { ->method('getUser') ->will($this->returnValue($this->user)); - $this->service = new UserStoragesService($userSession); + $this->service = new UserStoragesService($this->backendService, $userSession); // create home folder mkdir($this->dataDir . '/' . $this->userId . '/'); @@ -54,7 +54,8 @@ public function tearDown() { private function makeTestStorageData() { return $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -76,7 +77,8 @@ public function testAddStorage() { $newStorage = $this->service->getStorage(1); $this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint()); - $this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass()); + $this->assertEquals($storage->getBackend(), $newStorage->getBackend()); + $this->assertEquals($storage->getAuthMechanism(), $newStorage->getAuthMechanism()); $this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions()); $this->assertEquals(1, $newStorage->getId()); $this->assertEquals(0, $newStorage->getStatus()); @@ -98,7 +100,8 @@ public function testAddStorage() { public function testUpdateStorage() { $storage = $this->makeStorageConfig([ 'mountPoint' => 'mountpoint', - 'backendClass' => '\OC\Files\Storage\SMB', + 'backendIdentifier' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanismIdentifier' => 'identifier:\Auth\Mechanism', 'backendOptions' => [ 'option1' => 'value1', 'option2' => 'value2', @@ -191,7 +194,8 @@ public function testLegacyConfigConversion() { $mountPointOptions = current($mountPointData); $this->assertEquals(1, $mountPointOptions['id']); - $this->assertEquals('\OC\Files\Storage\SMB', $mountPointOptions['class']); + $this->assertEquals('identifier:\OCA\Files_External\Lib\Backend\SMB', $mountPointOptions['backend']); + $this->assertEquals('identifier:\Auth\Mechanism', $mountPointOptions['authMechanism']); $this->assertEquals(false, $mountPointOptions['mountOptions']['preview']); $backendOptions = $mountPointOptions['options']; @@ -214,13 +218,15 @@ public function testReadLegacyConfigAndGenerateConfigId() { $legacyBackendOptions = \OC_Mount_Config::encryptPasswords($legacyBackendOptions); $legacyConfig = [ - 'class' => '\OC\Files\Storage\SMB', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => false], ]; // different mount options $legacyConfig2 = [ - 'class' => '\OC\Files\Storage\SMB', + 'backend' => 'identifier:\OCA\Files_External\Lib\Backend\SMB', + 'authMechanism' => 'identifier:\Auth\Mechanism', 'options' => $legacyBackendOptions, 'mountOptions' => ['preview' => true], ]; diff --git a/apps/files_external/tests/storageconfigtest.php b/apps/files_external/tests/storageconfigtest.php index c30a4935ce17..dba5105d7db8 100644 --- a/apps/files_external/tests/storageconfigtest.php +++ b/apps/files_external/tests/storageconfigtest.php @@ -26,9 +26,22 @@ class StorageConfigTest extends \Test\TestCase { public function testJsonSerialization() { + $backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') + ->disableOriginalConstructor() + ->getMock(); + $backend->method('getIdentifier') + ->willReturn('storage::identifier'); + + $authMech = $this->getMockBuilder('\OCA\Files_External\Lib\Auth\AuthMechanism') + ->disableOriginalConstructor() + ->getMock(); + $authMech->method('getIdentifier') + ->willReturn('auth::identifier'); + $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('test'); - $storageConfig->setBackendClass('\OC\Files\Storage\SMB'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions(['user' => 'test', 'password' => 'password123']); $storageConfig->setPriority(128); $storageConfig->setApplicableUsers(['user1', 'user2']); @@ -39,7 +52,8 @@ public function testJsonSerialization() { $this->assertEquals(1, $json['id']); $this->assertEquals('/test', $json['mountPoint']); - $this->assertEquals('\OC\Files\Storage\SMB', $json['backendClass']); + $this->assertEquals('storage::identifier', $json['backend']); + $this->assertEquals('auth::identifier', $json['authMechanism']); $this->assertEquals('test', $json['backendOptions']['user']); $this->assertEquals('password123', $json['backendOptions']['password']); $this->assertEquals(128, $json['priority']); diff --git a/lib/private/files/mount/mountpoint.php b/lib/private/files/mount/mountpoint.php index 5e4949aa9ddb..2871bbd90835 100644 --- a/lib/private/files/mount/mountpoint.php +++ b/lib/private/files/mount/mountpoint.php @@ -29,7 +29,6 @@ use \OC\Files\Filesystem; use OC\Files\Storage\StorageFactory; use OC\Files\Storage\Storage; -use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\Mount\IMountPoint; class MountPoint implements IMountPoint { @@ -93,11 +92,7 @@ public function __construct($storage, $mountpoint, $arguments = null, $loader = $this->mountPoint = $mountpoint; if ($storage instanceof Storage) { $this->class = get_class($storage); - $this->storage = $storage; - // only wrap if not already wrapped - if (!($this->storage instanceof Wrapper)) { - $this->storage = $this->loader->wrap($this, $this->storage); - } + $this->storage = $this->loader->wrap($this, $storage); } else { // Update old classes to new namespace if (strpos($storage, 'OC_Filestorage_') !== false) { diff --git a/lib/private/files/storage/wrapper/permissionsmask.php b/lib/private/files/storage/wrapper/permissionsmask.php index 993936321d02..50c3f2a62682 100644 --- a/lib/private/files/storage/wrapper/permissionsmask.php +++ b/lib/private/files/storage/wrapper/permissionsmask.php @@ -65,6 +65,10 @@ public function isDeletable($path) { return $this->checkMask(Constants::PERMISSION_DELETE) and parent::isDeletable($path); } + public function isSharable($path) { + return $this->checkMask(Constants::PERMISSION_SHARE) and parent::isSharable($parm); + } + public function getPermissions($path) { return $this->storage->getPermissions($path) & $this->mask; } diff --git a/settings/css/settings.css b/settings/css/settings.css index 0af63821627b..4432bbd966b3 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -432,14 +432,16 @@ table.grid td.date{ } span.success { - background: #37ce02; - border-radius: 3px; + background: #37ce02; + border-radius: 3px; } - span.error { - background: #ce3702; + background: #ce3702; +} +span.indeterminate { + background: #e6db00; + border-radius: 40% 0; } - /* PASSWORD */ .strengthify-wrapper { diff --git a/settings/personal.php b/settings/personal.php index 203c9f68af83..bbbba3b5b8dd 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -108,10 +108,7 @@ $enableCertImport = false; $externalStorageEnabled = \OC::$server->getAppManager()->isEnabledForUser('files_external'); if ($externalStorageEnabled) { - $backends = OC_Mount_Config::getPersonalBackends(); - if (!empty($backends)) { - $enableCertImport = true; - } + $enableCertImport = true; } diff --git a/tests/lib/files/mount/mountpoint.php b/tests/lib/files/mount/mountpoint.php index d758c1b8d4d7..29610e6058da 100644 --- a/tests/lib/files/mount/mountpoint.php +++ b/tests/lib/files/mount/mountpoint.php @@ -70,25 +70,4 @@ public function testInvalidStorage() { // storage wrapper never called $this->assertFalse($called); } - - public function testWrappedStorage() { - $storage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Wrapper') - ->disableOriginalConstructor() - ->getMock(); - - $loader = $this->getMock('\OCP\Files\Storage\IStorageFactory'); - $loader->expects($this->never()) - ->method('getInstance'); - $loader->expects($this->never()) - ->method('wrap'); - - $mountPoint = new \OC\Files\Mount\MountPoint( - $storage, - '/mountpoint', - null, - $loader - ); - - $this->assertEquals($storage, $mountPoint->getStorage()); - } }