Merge pull request #8032 from owncloud/fs-backend-registration

Files External: allow dynamic backend registration
This commit is contained in:
Thomas Müller 2014-04-08 11:27:02 +02:00
commit 411a810ebe
10 changed files with 463 additions and 167 deletions

View File

@ -31,3 +31,136 @@ OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config
OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\iRODS', 'login');
OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login');
OC_Mount_Config::registerBackend('\OC\Files\Storage\Local', array(
'backend' => 'Local',
'configuration' => array(
'datadir' => 'Location')));
OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array(
'backend' => 'Amazon S3',
'configuration' => array(
'key' => 'Key',
'secret' => '*Secret',
'bucket' => 'Bucket'),
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array(
'backend' => 'Amazon S3 and compliant',
'configuration' => array(
'key' => 'Access Key',
'secret' => '*Secret Key',
'bucket' => 'Bucket',
'hostname' => '&Hostname (optional)',
'port' => '&Port (optional)',
'region' => '&Region (optional)',
'use_ssl' => '!Enable SSL',
'use_path_style' => '!Enable Path Style'),
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\Dropbox', array(
'backend' => 'Dropbox',
'configuration' => array(
'configured' => '#configured',
'app_key' => 'App key',
'app_secret' => '*App secret',
'token' => '#token',
'token_secret' => '#token_secret'),
'custom' => 'dropbox',
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\FTP', array(
'backend' => 'FTP',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root',
'secure' => '!Secure ftps://'),
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\Google', array(
'backend' => 'Google Drive',
'configuration' => array(
'configured' => '#configured',
'client_id' => 'Client ID',
'client_secret' => '*Client secret',
'token' => '#token'),
'custom' => 'google',
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\Swift', array(
'backend' => 'OpenStack Object Storage',
'configuration' => array(
'user' => 'Username (required)',
'bucket' => 'Bucket (required)',
'region' => '&Region (optional for OpenStack Object Storage)',
'key' => '*API Key (required for Rackspace Cloud Files)',
'tenant' => '&Tenantname (required for OpenStack Object Storage)',
'password' => '*Password (required for OpenStack Object Storage)',
'service_name' => '&Service Name (required for OpenStack Object Storage)',
'url' => '&URL of identity endpoint (required for OpenStack Object Storage)',
'timeout' => '&Timeout of HTTP requests in seconds (optional)',
),
'has_dependencies' => true));
if (!OC_Util::runningOnWindows()) {
OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB', array(
'backend' => 'SMB / CIFS',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'share' => 'Share',
'root' => '&Root'),
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB_OC', array(
'backend' => 'SMB / CIFS using OC login',
'configuration' => array(
'host' => 'URL',
'username_as_share' => '!Username as share',
'share' => '&Share',
'root' => '&Root'),
'has_dependencies' => true));
}
OC_Mount_Config::registerBackend('\OC\Files\Storage\DAV', array(
'backend' => 'WebDAV',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root',
'secure' => '!Secure https://'),
'has_dependencies' => true));
OC_Mount_Config::registerBackend('\OC\Files\Storage\OwnCloud', array(
'backend' => 'ownCloud',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Remote subfolder',
'secure' => '!Secure https://')));
OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP', array(
'backend' => 'SFTP',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root')));
OC_Mount_Config::registerBackend('\OC\Files\Storage\iRODS', array(
'backend' => 'iRODS',
'configuration' => array(
'host' => 'Host',
'port' => 'Port',
'use_logon_credentials' => '!Use ownCloud login',
'user' => 'Username',
'password' => '*Password',
'auth_mode' => 'Authentication Mode',
'zone' => 'Zone')));

View File

@ -548,4 +548,16 @@ class AmazonS3 extends \OC\Files\Storage\Common {
return false;
}
}
/**
* check if curl is installed
*/
public static function checkDependencies() {
if (function_exists('curl_init')) {
return true;
} else {
return array('curl');
}
}
}

View File

@ -39,6 +39,34 @@ class OC_Mount_Config {
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false;
private static $backends = array();
/**
* @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
*
* @return array of previously registered backends
*/
public static function setUp($backends = array()) {
$backup = self::$backends;
self::$backends = $backends;
return $backup;
}
/**
* 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
@ -46,132 +74,32 @@ class OC_Mount_Config {
* 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 string
* @return array
*/
public static function getBackends() {
$sortFunc = function($a, $b) {
return strcasecmp($a['backend'], $b['backend']);
};
// FIXME: do not rely on php key order for the options order in the UI
$backends['\OC\Files\Storage\Local']=array(
'backend' => 'Local',
'configuration' => array(
'datadir' => 'Location'));
$backEnds = array();
$backends['\OC\Files\Storage\AmazonS3']=array(
'backend' => 'Amazon S3 and compliant',
'configuration' => array(
'key' => 'Access Key',
'secret' => '*Secret Key',
'bucket' => 'Bucket',
'hostname' => '&Hostname (optional)',
'port' => '&Port (optional)',
'region' => '&Region (optional)',
'use_ssl' => '!Enable SSL',
'use_path_style' => '!Enable Path Style'));
$backends['\OC\Files\Storage\Dropbox']=array(
'backend' => 'Dropbox',
'configuration' => array(
'configured' => '#configured',
'app_key' => 'App key',
'app_secret' => '*App secret',
'token' => '#token',
'token_secret' => '#token_secret'),
'custom' => 'dropbox');
if(OC_Mount_Config::checkphpftp()) $backends['\OC\Files\Storage\FTP']=array(
'backend' => 'FTP',
'configuration' => array(
'host' => 'Hostname',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root',
'secure' => '!Secure ftps://'));
if(OC_Mount_Config::checkcurl()) $backends['\OC\Files\Storage\Google']=array(
'backend' => 'Google Drive',
'configuration' => array(
'configured' => '#configured',
'client_id' => 'Client ID',
'client_secret' => '*Client secret',
'token' => '#token'),
'custom' => 'google');
if(OC_Mount_Config::checkcurl()) {
$backends['\OC\Files\Storage\Swift'] = array(
'backend' => 'OpenStack Object Storage',
'configuration' => array(
'user' => 'Username (required)',
'bucket' => 'Bucket (required)',
'region' => '&Region (optional for OpenStack Object Storage)',
'key' => '*API Key (required for Rackspace Cloud Files)',
'tenant' => '&Tenantname (required for OpenStack Object Storage)',
'password' => '*Password (required for OpenStack Object Storage)',
'service_name' => '&Service Name (required for OpenStack Object Storage)',
'url' => '&URL of identity endpoint (required for OpenStack Object Storage)',
'timeout' => '&Timeout of HTTP requests in seconds (optional)',
)
);
}
if (!OC_Util::runningOnWindows()) {
if (OC_Mount_Config::checksmbclient()) {
$backends['\OC\Files\Storage\SMB'] = array(
'backend' => 'SMB / CIFS',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'share' => 'Share',
'root' => '&Root'));
$backends['\OC\Files\Storage\SMB_OC'] = array(
'backend' => 'SMB / CIFS using OC login',
'configuration' => array(
'host' => 'URL',
'username_as_share' => '!Username as share',
'share' => '&Share',
'root' => '&Root'));
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;
}
if(OC_Mount_Config::checkcurl()){
$backends['\OC\Files\Storage\DAV']=array(
'backend' => 'WebDAV',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root',
'secure' => '!Secure https://'));
$backends['\OC\Files\Storage\OwnCloud']=array(
'backend' => 'ownCloud',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Remote subfolder',
'secure' => '!Secure https://'));
}
uasort($backEnds, $sortFunc);
$backends['\OC\Files\Storage\SFTP']=array(
'backend' => 'SFTP',
'configuration' => array(
'host' => 'URL',
'user' => 'Username',
'password' => '*Password',
'root' => '&Root'));
$backends['\OC\Files\Storage\iRODS']=array(
'backend' => 'iRODS',
'configuration' => array(
'host' => 'Host',
'port' => 'Port',
'use_logon_credentials' => '!Use ownCloud login',
'user' => 'Username',
'password' => '*Password',
'auth_mode' => 'Authentication Mode',
'zone' => 'Zone'));
return($backends);
return $backEnds;
}
/**
@ -278,19 +206,19 @@ class OC_Mount_Config {
*/
public static function getPersonalBackends() {
$backends = self::getBackends();
$backEnds = self::getBackends();
// Remove local storage and other disabled storages
unset($backends['\OC\Files\Storage\Local']);
unset($backEnds['\OC\Files\Storage\Local']);
$allowed_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', ''));
foreach ($backends as $backend => $null) {
if (!in_array($backend, $allowed_backends)) {
unset($backends[$backend]);
$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;
return $backEnds;
}
/**
@ -373,7 +301,7 @@ class OC_Mount_Config {
*/
public static function getPersonalMountPoints() {
$mountPoints = self::readData(true);
$backends = self::getBackends();
$backEnds = self::getBackends();
$uid = OCP\User::getUser();
$personal = array();
if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) {
@ -387,7 +315,7 @@ class OC_Mount_Config {
'class' => $mount['class'],
// Remove '/uid/files/' from mount point
'mountpoint' => substr($mountPoint, strlen($uid) + 8),
'backend' => $backends[$mount['class']]['backend'],
'backend' => $backEnds[$mount['class']]['backend'],
'options' => $mount['options'],
'status' => self::getBackendStatus($mount['class'], $mount['options'], true)
);
@ -613,55 +541,92 @@ class OC_Mount_Config {
return true;
}
/**
* check if smbclient is installed
*/
public static function checksmbclient() {
if(function_exists('shell_exec')) {
$output=shell_exec('command -v smbclient 2> /dev/null');
return !empty($output);
}else{
return false;
}
}
/**
* check if php-ftp is installed
*/
public static function checkphpftp() {
if(function_exists('ftp_login')) {
return true;
}else{
return false;
}
}
/**
* check if curl is installed
*/
public static function checkcurl() {
return function_exists('curl_init');
}
/**
* check dependencies
*/
public static function checkDependencies() {
$l= new OC_L10N('files_external');
$txt='';
if (!OC_Util::runningOnWindows()) {
if(!OC_Mount_Config::checksmbclient()) {
$txt.=$l->t('<b>Warning:</b> "smbclient" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it.').'<br />';
$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(!OC_Mount_Config::checkphpftp()) {
$txt.=$l->t('<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it.').'<br />';
if (count($dependencies) > 0) {
return OC_Mount_Config::generateDependencyMessage($dependencies);
}
if(!OC_Mount_Config::checkcurl()) {
$txt.=$l->t('<b>Warning:</b> The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it.').'<br />';
return '';
}
private static function addDependency(&$dependencies, $module, $backend, $message=null) {
if (!isset($dependencies[$module])) {
$dependencies[$module] = array();
}
return $txt;
if ($message === null) {
$dependencies[$module][] = $backend;
} else {
$dependencies[$module][] = array('backend' => $backend, 'message' => $message);
}
}
private static function generateDependencyMessage($dependencies) {
$l = new \OC_L10N('files_external');
$dependencyMessage = '';
foreach ($dependencies as $module => $backends) {
$dependencyGroup = array();
foreach ($backends as $backend) {
if (is_array($backend)) {
$dependencyMessage .= '<br />' . $l->t('<b>Note:</b> ') . $backend['message'];
} else {
$dependencyGroup[] = $backend;
}
}
if (count($dependencyGroup) > 0) {
$backends = '';
for ($i = 0; $i < count($dependencyGroup); $i++) {
if ($i > 0 && $i === count($dependencyGroup) - 1) {
$backends .= $l->t(' and ');
} elseif ($i > 0) {
$backends .= ', ';
}
$backends .= '<i>' . $dependencyGroup[$i] . '</i>';
}
$dependencyMessage .= '<br />' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends);
}
}
return $dependencyMessage;
}
/**
* Returns a dependency missing message
* @param $l OC_L10N
* @param $module string
* @param $backend string
* @return string
*/
private static function getSingleDependencyMessage($l, $module, $backend) {
switch (strtolower($module)) {
case 'curl':
return $l->t('<b>Note:</b> The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.', $backend);
case 'ftp':
return $l->t('<b>Note:</b> The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.', $backend);
default:
return $l->t('<b>Note:</b> "%s" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it.', array($module, $backend));
}
}
/**

View File

@ -311,4 +311,15 @@ class Dropbox extends \OC\Files\Storage\Common {
return true;
}
/**
* check if curl is installed
*/
public static function checkDependencies() {
if (function_exists('curl_init')) {
return true;
} else {
return array('curl');
}
}
}

View File

@ -119,4 +119,16 @@ class FTP extends \OC\Files\Storage\StreamWrapper{
unlink($tmpFile);
}
}
/**
* check if php-ftp is installed
*/
public static function checkDependencies() {
if (function_exists('ftp_login')) {
return(true);
} else {
return array('ftp');
}
}
}

View File

@ -586,4 +586,15 @@ class Google extends \OC\Files\Storage\Common {
return false;
}
/**
* check if curl is installed
*/
public static function checkDependencies() {
if (function_exists('curl_init')) {
return true;
} else {
return array('curl');
}
}
}

View File

@ -134,4 +134,18 @@ class SMB extends \OC\Files\Storage\StreamWrapper{
}
return $lastCtime;
}
/**
* check if smbclient is installed
*/
public static function checkDependencies() {
if (function_exists('shell_exec')) {
$output=shell_exec('command -v smbclient 2> /dev/null');
if (!empty($output)) {
return true;
}
}
return array('smbclient');
}
}

View File

@ -65,6 +65,18 @@ class Swift extends \OC\Files\Storage\Common {
return $path;
}
const SUBCONTAINER_FILE='.subcontainers';
/**
* translate directory path to container name
* @param string $path
* @return string
*/
private function getContainerName($path) {
$path=trim(trim($this->root, '/') . "/".$path, '/.');
return str_replace('/', '\\', $path);
}
/**
* @param string $path
*/
@ -490,4 +502,16 @@ class Swift extends \OC\Files\Storage\Common {
), $tmpFile);
unlink($tmpFile);
}
/**
* check if curl is installed
*/
public static function checkDependencies() {
if (function_exists('curl_init')) {
return true;
} else {
return array('curl');
}
}
}

View File

@ -385,5 +385,16 @@ class DAV extends \OC\Files\Storage\Common {
return false;
}
}
/**
* check if curl is installed
*/
public static function checkDependencies() {
if (function_exists('curl_init')) {
return true;
} else {
return array('curl');
}
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* ownCloud
*
* @author Thomas Müller
* Copyright (c) 2014 Thomas Müller <deepdiver@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
require_once __DIR__ . '/../../../lib/base.php';
require __DIR__ . '/../lib/config.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 \PHPUnit_Framework_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('<br /><b>Note:</b> "dummy" is not installed. Mounting of <i>Test Dummy</i> is not possible. Please ask your system administrator to install it.',
$message);
}
protected function 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);
}
}