diff options
31 files changed, 720 insertions, 744 deletions
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 9db4b0a6330..f33012b5f09 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -37,11 +37,9 @@ OC::$CLASSPATH['OC\Files\Storage\OwnCloud'] = 'files_external/lib/owncloud.php'; 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'; @@ -68,51 +66,6 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == ' // 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\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_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\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 = $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 aa9e4fd913e..3a222141fb5 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -24,7 +24,6 @@ namespace OCA\Files_External\AppInfo; -use \OCA\Files_External\Controller\AjaxController; use \OCP\AppFramework\App; use \OCP\IContainer; use \OCA\Files_External\Service\BackendService; @@ -36,18 +35,6 @@ class Application extends App { public function __construct(array $urlParams=array()) { parent::__construct('files_external', $urlParams); - $container = $this->getContainer(); - - /** - * Controllers - */ - $container->registerService('AjaxController', function (IContainer $c) { - return new AjaxController( - $c->query('AppName'), - $c->query('Request') - ); - }); - $this->loadBackends(); $this->loadAuthMechanisms(); @@ -73,11 +60,14 @@ class Application extends App { $container->query('OCA\Files_External\Lib\Backend\AmazonS3'), $container->query('OCA\Files_External\Lib\Backend\Dropbox'), $container->query('OCA\Files_External\Lib\Backend\Google'), + $container->query('OCA\Files_External\Lib\Backend\Swift'), + $container->query('OCA\Files_External\Lib\Backend\SFTP_Key'), ]); if (!\OC_Util::runningOnWindows()) { $service->registerBackends([ $container->query('OCA\Files_External\Lib\Backend\SMB'), + $container->query('OCA\Files_External\Lib\Backend\SMB_OC'), ]); } } @@ -106,6 +96,13 @@ class Application extends App { // 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 5d7018c3476..a371273e74e 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -38,7 +38,7 @@ namespace OCA\Files_External\AppInfo; 'routes' => array( array( 'name' => 'Ajax#getSshKeys', - 'url' => '/ajax/sftp_key.php', + 'url' => '/ajax/public_key.php', 'verb' => 'POST', 'requirements' => array() ) diff --git a/apps/files_external/controller/ajaxcontroller.php b/apps/files_external/controller/ajaxcontroller.php index cb2de432286..c285cd34e70 100644 --- a/apps/files_external/controller/ajaxcontroller.php +++ b/apps/files_external/controller/ajaxcontroller.php @@ -25,19 +25,19 @@ namespace OCA\Files_External\Controller; 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 756a34fc5d4..7d97fdbb4f4 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -32,6 +32,7 @@ use \OCP\AppFramework\Http; use \OCA\Files_external\Service\GlobalStoragesService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Service\BackendService; /** * Global storages controller @@ -97,7 +98,7 @@ class GlobalStoragesController extends StoragesController { return $newStorage; } - $response = $this->validate($newStorage); + $response = $this->validate($newStorage, BackendService::PERMISSION_CREATE); if (!empty($response)) { return $response; } @@ -153,7 +154,7 @@ class GlobalStoragesController extends StoragesController { } $storage->setId($id); - $response = $this->validate($storage); + $response = $this->validate($storage, BackendService::PERMISSION_MODIFY); if (!empty($response)) { return $response; } @@ -178,4 +179,14 @@ class GlobalStoragesController extends StoragesController { } + /** + * Get the user type for this controller, used in validation + * + * @return string BackendService::USER_* constants + */ + protected function getUserType() { + return BackendService::USER_ADMIN; + } + + } diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 613f22c0331..46202c8ba4a 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -36,6 +36,7 @@ use \OCA\Files_External\Lib\Backend\Backend; use \OCA\Files_External\Lib\Auth\AuthMechanism; use \OCP\Files\StorageNotAvailableException; use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; +use \OCA\Files_External\Service\BackendService; /** * Base class for storages controllers @@ -124,10 +125,11 @@ abstract class StoragesController extends Controller { * Validate storage config * * @param StorageConfig $storage storage config + * @param int $permissionCheck permission to check * * @return DataResponse|null returns response in case of validation error */ - protected function validate(StorageConfig $storage) { + protected function validate(StorageConfig $storage, $permissionCheck = BackendService::PERMISSION_CREATE) { $mountPoint = $storage->getMountPoint(); if ($mountPoint === '' || $mountPoint === '/') { return new DataResponse( @@ -157,12 +159,36 @@ abstract class StoragesController extends Controller { return new DataResponse( array( 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [ - $storage->getBackend()->getIdentifier() + $backend->getIdentifier() ]) ), Http::STATUS_UNPROCESSABLE_ENTITY ); } + + if (!$backend->isPermitted($this->getUserType(), $permissionCheck)) { + // not permitted to use backend + return new DataResponse( + array( + 'message' => (string)$this->l10n->t('Not permitted to use backend "%s"', [ + $backend->getIdentifier() + ]) + ), + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + if (!$authMechanism->isPermitted($this->getUserType(), $permissionCheck)) { + // not permitted to use auth mechanism + return new DataResponse( + array( + 'message' => (string)$this->l10n->t('Not permitted to use authentication mechanism "%s"', [ + $authMechanism->getIdentifier() + ]) + ), + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + if (!$backend->validateStorage($storage)) { // unsatisfied parameters return new DataResponse( @@ -186,6 +212,13 @@ abstract class StoragesController extends Controller { } /** + * Get the user type for this controller, used in validation + * + * @return string BackendService::USER_* constants + */ + abstract protected function getUserType(); + + /** * Check whether the given storage is available / valid. * * Note that this operation can be time consuming depending diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index 0d41e088a76..801c9ab0aae 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -62,38 +62,6 @@ class UserStoragesController extends StoragesController { } /** - * Validate storage config - * - * @param StorageConfig $storage storage config - * - * @return DataResponse|null returns response in case of validation error - */ - protected function validate(StorageConfig $storage) { - $result = parent::validate($storage); - - 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 - /** @var Backend */ - $backend = $storage->getBackend(); - if (!$backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) { - return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Admin-only storage backend "%s"', [ - $storage->getBackend()->getIdentifier() - ]) - ), - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - - return null; - } - - /** * Return storage * * @NoAdminRequired @@ -135,7 +103,7 @@ class UserStoragesController extends StoragesController { return $newStorage; } - $response = $this->validate($newStorage); + $response = $this->validate($newStorage, BackendService::PERMISSION_CREATE); if (!empty($response)) { return $response; } @@ -183,7 +151,7 @@ class UserStoragesController extends StoragesController { } $storage->setId($id); - $response = $this->validate($storage); + $response = $this->validate($storage, BackendService::PERMISSION_MODIFY); if (!empty($response)) { return $response; } @@ -218,4 +186,14 @@ class UserStoragesController extends StoragesController { public function destroy($id) { return parent::destroy($id); } + + /** + * Get the user type for this controller, used in validation + * + * @return string BackendService::USER_* constants + */ + protected function getUserType() { + return BackendService::USER_PERSONAL; + } + } diff --git a/apps/files_external/js/public_key.js b/apps/files_external/js/public_key.js new file mode 100644 index 00000000000..a8546067452 --- /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/sftp_key.js b/apps/files_external/js/sftp_key.js deleted file mode 100644 index 55b11b1fac9..00000000000 --- 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/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php index 11d99bb330d..ddc0c6a4dca 100644 --- a/apps/files_external/lib/auth/authmechanism.php +++ b/apps/files_external/lib/auth/authmechanism.php @@ -22,7 +22,7 @@ namespace OCA\Files_External\Lib\Auth; use \OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\PermissionsTrait; use \OCA\Files_External\Lib\IdentifierTrait; use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\StorageModifierTrait; @@ -40,7 +40,7 @@ use \OCA\Files_External\Lib\StorageModifierTrait; * scheme, which are provided from the authentication mechanism. * * This class uses the following traits: - * - VisibilityTrait + * - PermissionsTrait * Restrict usage to admin-only/none * - FrontendDefinitionTrait * Specify configuration parameters and other definitions @@ -58,7 +58,7 @@ class AuthMechanism implements \JsonSerializable { const SCHEME_PUBLICKEY = 'publickey'; const SCHEME_OPENSTACK = 'openstack'; - use VisibilityTrait; + use PermissionsTrait; use FrontendDefinitionTrait; use StorageModifierTrait; use IdentifierTrait; 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 00000000000..faf356bcf2e --- /dev/null +++ b/apps/files_external/lib/auth/openstack/openstack.php @@ -0,0 +1,48 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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 00000000000..9268f3aad87 --- /dev/null +++ b/apps/files_external/lib/auth/openstack/rackspace.php @@ -0,0 +1,46 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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/publickey/rsa.php b/apps/files_external/lib/auth/publickey/rsa.php new file mode 100644 index 00000000000..f40136dda01 --- /dev/null +++ b/apps/files_external/lib/auth/publickey/rsa.php @@ -0,0 +1,80 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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 = 1024; + + /** @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/backend.php b/apps/files_external/lib/backend/backend.php index 90d5d38ed94..2a2add3ac59 100644 --- a/apps/files_external/lib/backend/backend.php +++ b/apps/files_external/lib/backend/backend.php @@ -22,7 +22,7 @@ namespace OCA\Files_External\Lib\Backend; use \OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\VisibilityTrait; +use \OCA\Files_External\Lib\PermissionsTrait; use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\PriorityTrait; use \OCA\Files_External\Lib\DependencyTrait; @@ -43,7 +43,7 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; * scheme, which are provided from the authentication mechanism. * * This class uses the following traits: - * - VisibilityTrait + * - PermissionsTrait * Restrict usage to admin-only/none * - FrontendDefinitionTrait * Specify configuration parameters and other definitions @@ -56,7 +56,7 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; */ class Backend implements \JsonSerializable { - use VisibilityTrait; + use PermissionsTrait; use FrontendDefinitionTrait; use PriorityTrait; use DependencyTrait; diff --git a/apps/files_external/lib/backend/local.php b/apps/files_external/lib/backend/local.php index a80b437fab7..a6635491b6e 100644 --- a/apps/files_external/lib/backend/local.php +++ b/apps/files_external/lib/backend/local.php @@ -39,7 +39,7 @@ class Local extends Backend { ->addParameters([ (new DefinitionParameter('datadir', $l->t('Location'))), ]) - ->setAllowedVisibility(BackendService::VISIBILITY_ADMIN) + ->setAllowedPermissions(BackendService::USER_PERSONAL, BackendService::PERMISSION_NONE) ->setPriority(BackendService::PRIORITY_DEFAULT + 50) ->addAuthScheme(AuthMechanism::SCHEME_NULL) ->setLegacyAuthMechanism($legacyAuth) diff --git a/apps/files_external/lib/backend/sftp.php b/apps/files_external/lib/backend/sftp.php index dd0f5d8e2e0..c0bcd27c54b 100644 --- a/apps/files_external/lib/backend/sftp.php +++ b/apps/files_external/lib/backend/sftp.php @@ -43,6 +43,7 @@ class SFTP extends Backend { ->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 00000000000..6a75172026d --- /dev/null +++ b/apps/files_external/lib/backend/sftp_key.php @@ -0,0 +1,50 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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), + ]) + ->removeAllowedPermission(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) + ->removeAllowedPermission(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE) + ->addAuthScheme(AuthMechanism::SCHEME_PUBLICKEY) + ->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 00000000000..3621682fb8f --- /dev/null +++ b/apps/files_external/lib/backend/smb_oc.php @@ -0,0 +1,69 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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') + ->removeAllowedPermission(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) + ->removeAllowedPermission(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE) + ->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 ($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 00000000000..c0ac7c08345 --- /dev/null +++ b/apps/files_external/lib/backend/swift.php @@ -0,0 +1,60 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +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['options']['key']) && $params['options']['key']) { + return $rackspaceAuth; + } + return $openstackAuth; + }) + ; + } + +} diff --git a/apps/files_external/lib/permissionstrait.php b/apps/files_external/lib/permissionstrait.php new file mode 100644 index 00000000000..8509a01e422 --- /dev/null +++ b/apps/files_external/lib/permissionstrait.php @@ -0,0 +1,164 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * + * @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 <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement backend and auth mechanism permissions + * + * For user type constants, see BackendService::USER_* + * For permission constants, see BackendService::PERMISSION_* + */ +trait PermissionsTrait { + + /** @var array [user type => permissions] */ + protected $permissions = [ + BackendService::USER_PERSONAL => BackendService::PERMISSION_DEFAULT, + BackendService::USER_ADMIN => BackendService::PERMISSION_DEFAULT, + ]; + + /** @var array [user type => allowed permissions] */ + protected $allowedPermissions = [ + BackendService::USER_PERSONAL => BackendService::PERMISSION_DEFAULT, + BackendService::USER_ADMIN => BackendService::PERMISSION_DEFAULT, + ]; + + /** + * @param string $userType + * @return int + */ + public function getPermissions($userType) { + if (isset($this->permissions[$userType])) { + return $this->permissions[$userType]; + } + return BackendService::PERMISSION_NONE; + } + + /** + * Check if the user type has permission + * + * @param string $userType + * @param int $permission + * @return bool + */ + public function isPermitted($userType, $permission) { + if ($this->getPermissions($userType) & $permission) { + return true; + } + return false; + } + + /** + * @param string $userType + * @param int $permissions + * @return self + */ + public function setPermissions($userType, $permissions) { + $this->permissions[$userType] = $permissions; + $this->allowedPermissions[$userType] = + $this->getAllowedPermissions($userType) | $permissions; + return $this; + } + + /** + * @param string $userType + * @param int $permission + * @return self + */ + public function addPermission($userType, $permission) { + return $this->setPermissions($userType, + $this->getPermissions($userType) | $permission + ); + } + + /** + * @param string $userType + * @param int $permission + * @return self + */ + public function removePermission($userType, $permission) { + return $this->setPermissions($userType, + $this->getPermissions($userType) & ~$permission + ); + } + + /** + * @param string $userType + * @return int + */ + public function getAllowedPermissions($userType) { + if (isset($this->allowedPermissions[$userType])) { + return $this->allowedPermissions[$userType]; + } + return BackendService::PERMISSION_NONE; + } + + /** + * Check if the user type has an allowed permission + * + * @param string $userType + * @param int $permission + * @return bool + */ + public function isAllowedPermitted($userType, $permission) { + if ($this->getAllowedPermissions($userType) & $permission) { + return true; + } + return false; + } + + /** + * @param string $userType + * @param int $permissions + * @return self + */ + public function setAllowedPermissions($userType, $permissions) { + $this->allowedPermissions[$userType] = $permissions; + $this->permissions[$userType] = + $this->getPermissions($userType) & $permissions; + return $this; + } + + /** + * @param string $userType + * @param int $permission + * @return self + */ + public function addAllowedPermission($userType, $permission) { + return $this->setAllowedPermissions($userType, + $this->getAllowedPermissions($userType) | $permission + ); + } + + /** + * @param string $userType + * @param int $permission + * @return self + */ + public function removeAllowedPermission($userType, $permission) { + return $this->setAllowedPermissions($userType, + $this->getAllowedPermissions($userType) & ~$permission + ); + } + +} diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index 7f921b5342f..921e7283c66 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -40,10 +40,11 @@ use phpseclib\Net\SFTP\Stream; 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 @@ class SFTP extends \OC\Files\Storage\Common { } $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 @@ class SFTP extends \OC\Files\Storage\Common { $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 @@ class SFTP extends \OC\Files\Storage\Common { 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 a193b323678..00000000000 --- a/apps/files_external/lib/sftp_key.php +++ /dev/null @@ -1,215 +0,0 @@ -<?php -/** - * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Ross Nicoll <jrn@jrn.me.uk> - * - * @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 <http://www.gnu.org/licenses/> - * - */ -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 547caa5ecbf..00000000000 --- a/apps/files_external/lib/smb_oc.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php -/** - * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <icewind@owncloud.com> - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> - * - * @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 <http://www.gnu.org/licenses/> - * - */ - -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/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php deleted file mode 100644 index dfd2d323ca6..00000000000 --- a/apps/files_external/lib/visibilitytrait.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> - * - * @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 <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Files_External\Lib; - -use \OCA\Files_External\Service\BackendService; - -/** - * Trait to implement visibility mechanics for a configuration class - * - * The standard visibility defines which users/groups can use or see the - * object. The allowed visibility defines the maximum visibility allowed to be - * set on the object. The standard visibility is often set dynamically by - * stored configuration parameters that can be modified by the administrator, - * while the allowed visibility is set directly by the object and cannot be - * modified by the administrator. - */ -trait VisibilityTrait { - - /** @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 8717d91d4f1..d47f983b357 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -34,8 +34,12 @@ $userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStor OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); -$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL); -$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_PERSONAL); +$backends = array_filter($backendService->getAvailableBackends(), function($backend) { + return $backend->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE); +}); +$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { + return $authMechanism->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE); +}); foreach ($backends as $backend) { if ($backend->getCustomJs()) { \OCP\Util::addScript('files_external', $backend->getCustomJs()); diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php index 382834b4c19..70cb9291660 100644 --- a/apps/files_external/service/backendservice.php +++ b/apps/files_external/service/backendservice.php @@ -31,13 +31,17 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; */ class BackendService { - /** Visibility constants for VisibilityTrait */ - const VISIBILITY_NONE = 0; - const VISIBILITY_PERSONAL = 1; - const VISIBILITY_ADMIN = 2; - //const VISIBILITY_ALIENS = 4; + /** Permission constants for PermissionsTrait */ + const PERMISSION_NONE = 0; + const PERMISSION_MOUNT = 1; + const PERMISSION_CREATE = 2; + const PERMISSION_MODIFY = 4; - const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN + const PERMISSION_DEFAULT = 7; // MOUNT | CREATE | MODIFY + + /** User contants */ + const USER_ADMIN = 'admin'; + const USER_PERSONAL = 'personal'; /** Priority constants for PriorityTrait */ const PRIORITY_DEFAULT = 100; @@ -81,7 +85,7 @@ class BackendService { */ public function registerBackend(Backend $backend) { if (!$this->isAllowedUserBackend($backend)) { - $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL); + $backend->removePermission(self::USER_PERSONAL, self::PERMISSION_CREATE | self::PERMISSION_MOUNT); } foreach ($backend->getIdentifierAliases() as $alias) { $this->backends[$alias] = $backend; @@ -103,7 +107,7 @@ class BackendService { */ public function registerAuthMechanism(AuthMechanism $authMech) { if (!$this->isAllowedAuthMechanism($authMech)) { - $authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL); + $authMech->removePermission(self::USER_PERSONAL, self::PERMISSION_CREATE | self::PERMISSION_MOUNT); } foreach ($authMech->getIdentifierAliases() as $alias) { $this->authMechanisms[$alias] = $authMech; @@ -145,30 +149,6 @@ class BackendService { } /** - * 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 */ @@ -206,31 +186,6 @@ class BackendService { } /** - * 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 */ diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 29c0553158f..840f1325fb5 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -41,8 +41,12 @@ OCP\Util::addStyle('files_external', 'settings'); \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); -$backends = $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN); -$authMechanisms = $backendService->getAuthMechanismsVisibleFor(BackendService::VISIBILITY_ADMIN); +$backends = array_filter($backendService->getAvailableBackends(), function($backend) { + return $backend->isPermitted(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE); +}); +$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { + return $authMechanism->isPermitted(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE); +}); foreach ($backends as $backend) { if ($backend->getCustomJs()) { \OCP\Util::addScript('files_external', $backend->getCustomJs()); @@ -54,13 +58,19 @@ foreach ($authMechanisms as $authMechanism) { } } +$userBackends = array_filter($backendService->getAvailableBackends(), function($backend) { + return $backend->isAllowedPermitted( + BackendService::USER_PERSONAL, BackendService::PERMISSION_MOUNT + ); +}); + $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', true); $tmpl->assign('storages', $globalStoragesService->getAllStorages()); $tmpl->assign('backends', $backends); $tmpl->assign('authMechanisms', $authMechanisms); -$tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL)); +$tmpl->assign('userBackends', $userBackends); $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 5b79edf6cf7..63a3a19de2f 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -197,7 +197,7 @@ <p id="userMountingBackends"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>> <?php p($l->t('Allow users to mount the following external storage')); ?><br /> <?php $i = 0; foreach ($_['userBackends'] as $backend): ?> - <input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> /> + <input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_MOUNT)) print_unescaped(' checked="checked"'); ?> /> <label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br /> <?php $i++; ?> <?php endforeach; ?> diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index 2b0ee7a14ea..c43761f3bcb 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -75,10 +75,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); + $authMech->method('isPermitted') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isVisibleFor') + $backend->method('isPermitted') ->willReturn(true); $storageConfig = new StorageConfig(1); @@ -114,10 +116,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); + $authMech->method('isPermitted') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isVisibleFor') + $backend->method('isPermitted') ->willReturn(true); $storageConfig = new StorageConfig(1); @@ -245,10 +249,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); + $authMech->method('isPermitted') + ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isVisibleFor') + $backend->method('isPermitted') ->willReturn(true); $storageConfig = new StorageConfig(255); @@ -332,12 +338,14 @@ abstract class StoragesControllerTest extends \Test\TestCase { $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn($backendValidate); - $backend->method('isVisibleFor') + $backend->method('isPermitted') ->willReturn(true); $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->will($this->returnValue($authMechValidate)); + $authMech->method('isPermitted') + ->willReturn(true); $storageConfig = new StorageConfig(); $storageConfig->setMountPoint('mount'); diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php index 9f1a8df8d2e..b61174b0797 100644 --- a/apps/files_external/tests/controller/userstoragescontrollertest.php +++ b/apps/files_external/tests/controller/userstoragescontrollertest.php @@ -49,15 +49,21 @@ class UserStoragesControllerTest extends StoragesControllerTest { } public function testAddOrUpdateStorageDisallowedBackend() { - $backend = $this->getBackendMock(); - $backend->method('isVisibleFor') - ->with(BackendService::VISIBILITY_PERSONAL) + $backend1 = $this->getBackendMock(); + $backend1->expects($this->once()) + ->method('isPermitted') + ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) + ->willReturn(false); + $backend2 = $this->getBackendMock(); + $backend2->expects($this->once()) + ->method('isPermitted') + ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_MODIFY) ->willReturn(false); $authMech = $this->getAuthMechMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); - $storageConfig->setBackend($backend); + $storageConfig->setBackend($backend1); $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); @@ -82,6 +88,8 @@ class UserStoragesControllerTest extends StoragesControllerTest { $this->assertEquals(Http::STATUS_UNPROCESSABLE_ENTITY, $response->getStatus()); + $storageConfig->setBackend($backend2); + $response = $this->controller->update( 1, 'mount', diff --git a/apps/files_external/tests/service/backendservicetest.php b/apps/files_external/tests/service/backendservicetest.php index 08f6b9bf988..b37b5e9b466 100644 --- a/apps/files_external/tests/service/backendservicetest.php +++ b/apps/files_external/tests/service/backendservicetest.php @@ -83,11 +83,11 @@ class BackendServiceTest extends \Test\TestCase { $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); $backendAllowed->expects($this->never()) - ->method('removeVisibility'); + ->method('removePermission'); $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); $backendNotAllowed->expects($this->once()) - ->method('removeVisibility') - ->with(BackendService::VISIBILITY_PERSONAL); + ->method('removePermission') + ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE | BackendService::PERMISSION_MOUNT); $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') ->disableOriginalConstructor() @@ -126,27 +126,5 @@ class BackendServiceTest extends \Test\TestCase { $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); - } - } |