From 860d51487b103f4c050d2427c35ee70dd4b2b05e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 19 Jan 2016 14:16:11 +0100 Subject: [PATCH] Allow setting user provided credentials from the personal settings page --- .../controller/storagescontroller.php | 2 + .../controller/usercredentialscontroller.php | 49 +++++++- .../controller/userstoragescontroller.php | 1 + apps/files_external/js/settings.js | 115 +++++++++++++++--- .../lib/auth/password/userprovided.php | 9 +- .../lib/definitionparameter.php | 48 ++++---- 6 files changed, 180 insertions(+), 44 deletions(-) diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 64b989f0c7..b09774ef51 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -25,6 +25,7 @@ namespace OCA\Files_External\Controller; use \OCP\IConfig; +use OCP\IUser; use \OCP\IUserSession; use \OCP\IRequest; use \OCP\IL10N; @@ -114,6 +115,7 @@ abstract class StoragesController extends Controller { $priority ); } catch (\InvalidArgumentException $e) { + \OC::$server->getLogger()->logException($e); return new DataResponse( [ 'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class') diff --git a/apps/files_external/controller/usercredentialscontroller.php b/apps/files_external/controller/usercredentialscontroller.php index bc514cba08..5153189d9c 100644 --- a/apps/files_external/controller/usercredentialscontroller.php +++ b/apps/files_external/controller/usercredentialscontroller.php @@ -21,12 +21,19 @@ namespace OCA\Files_External\Controller; +use OCA\Calendar\Sabre\Backend; +use OCA\Files_External\Lib\Auth\AuthMechanism; use OCA\Files_External\Lib\Auth\Password\UserProvided; +use OCA\Files_external\Lib\StorageConfig; +use OCA\Files_External\Service\UserGlobalStoragesService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; use OCP\IRequest; use OCP\IUserSession; -class UserCredentialsController extends Controller { +class UserCredentialsController extends StoragesController { /** * @var UserProvided */ @@ -37,10 +44,22 @@ class UserCredentialsController extends Controller { */ private $userSession; - public function __construct($appName, IRequest $request, UserProvided $authMechanism, IUserSession $userSession) { - parent::__construct($appName, $request); + /** + * @var UserGlobalStoragesService + */ + private $globalStoragesService; + + public function __construct( + $appName, IRequest $request, + UserProvided $authMechanism, + IUserSession $userSession, + IL10N $l10n, + UserGlobalStoragesService $globalStoragesService + ) { + parent::__construct($appName, $request, $l10n, $globalStoragesService); $this->authMechanism = $authMechanism; $this->userSession = $userSession; + $this->globalStoragesService = $globalStoragesService; } /** @@ -49,8 +68,32 @@ class UserCredentialsController extends Controller { * @param string $password * * @NoAdminRequired + * @return DataResponse */ public function store($storageId, $username, $password) { $this->authMechanism->saveCredentials($this->userSession->getUser(), $storageId, $username, $password); + + $storage = $this->globalStoragesService->getStorage($storageId); + + $this->updateStorageStatus($storage); + + $storage->setBackendOptions([]); + $storage->setMountOptions([]); + $this->manipulateStorageConfig($storage); + + + return new DataResponse( + $storage, + Http::STATUS_OK + ); + } + + protected function manipulateStorageConfig(StorageConfig $storage) { + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); + /** @var Backend */ + $backend = $storage->getBackend(); + $backend->manipulateStorageConfig($storage, $this->userSession->getUser()); } } diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index 741e906dec..ccf8bc24e0 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -25,6 +25,7 @@ namespace OCA\Files_External\Controller; use OCA\Files_External\Lib\Auth\AuthMechanism; use \OCP\IConfig; +use OCP\IUser; use \OCP\IUserSession; use \OCP\IRequest; use \OCP\IL10N; diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 756804238d..16576ce708 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -357,6 +357,9 @@ StorageConfig.prototype = { if (this.mountPoint === '') { return false; } + if (!this.backend) { + return false; + } if (this.errors) { return false; } @@ -432,6 +435,48 @@ UserStorageConfig.prototype = _.extend({}, StorageConfig.prototype, _url: 'apps/files_external/userstorages' }); +/** + * @class OCA.External.Settings.UserGlobalStorageConfig + * @augments OCA.External.Settings.StorageConfig + * + * @classdesc User external storage config + */ +var UserGlobalStorageConfig = function (id) { + this.id = id; +}; +UserGlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype, + /** @lends OCA.External.Settings.UserStorageConfig.prototype */ { + + _url: 'apps/files_external/userglobalstorages', + + /** + * Creates or saves the storage. + * + * @param {Function} [options.success] success callback, receives result as argument + * @param {Function} [options.error] error callback + */ + save: function (options) { + var self = this; + var url = OC.generateUrl('apps/files_external/usercredentials/{id}', {id: this.id}); + + $.ajax({ + type: 'PUT', + url: url, + contentType: 'application/json', + data: JSON.stringify({ + username: this.backendOptions.user, + password: this.backendOptions.password + }), + success: function (result) { + if (_.isFunction(options.success)) { + options.success(result); + } + }, + error: options.error + }); + } +}); + /** * @class OCA.External.Settings.MountOptionsDropdown * @@ -748,7 +793,7 @@ MountConfigListView.prototype = _.extend({ $.each(authMechanismConfiguration['configuration'], _.partial( this.writeParameterInput, $td, _, _, ['auth-param'] - )); + ).bind(this)); this.trigger('selectAuthMechanism', $tr, authMechanism, authMechanismConfiguration['scheme'], onCompletion @@ -770,6 +815,7 @@ MountConfigListView.prototype = _.extend({ var $tr = this.$el.find('tr#addMountPoint'); this.$el.find('tbody').append($tr.clone()); + $tr.data('storageConfig', storageConfig); $tr.find('td').last().attr('class', 'remove'); $tr.find('td.mountOptionsToggle').removeClass('hidden'); $tr.find('td').last().removeAttr('style'); @@ -805,7 +851,7 @@ MountConfigListView.prototype = _.extend({ $tr.find('td.authentication').append(selectAuthMechanism); var $td = $tr.find('td.configuration'); - $.each(backend.configuration, _.partial(this.writeParameterInput, $td)); + $.each(backend.configuration, _.partial(this.writeParameterInput, $td).bind(this)); this.trigger('selectBackend', $tr, backend.identifier, onCompletion); this.configureAuthMechanism($tr, storageConfig.authMechanism, onCompletion); @@ -866,8 +912,14 @@ MountConfigListView.prototype = _.extend({ success: function(result) { var onCompletion = jQuery.Deferred(); $.each(result, function(i, storageParams) { + var storageConfig; + var isUserProvidedAuth = storageParams.authMechanism === 'password::userprovided'; storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash - var storageConfig = new self._storageConfigClass(); + if (isUserProvidedAuth) { + storageConfig = new UserGlobalStorageConfig(); + } else { + storageConfig = new self._storageConfigClass(); + } _.extend(storageConfig, storageParams); var $tr = self.newStorage(storageConfig, onCompletion); @@ -878,12 +930,16 @@ MountConfigListView.prototype = _.extend({ var $authentication = $tr.find('.authentication'); $authentication.text($authentication.find('select option:selected').text()); - // userglobal storages do not expose configuration data - $tr.find('.configuration').text(t('files_external', 'Admin defined')); - // disable any other inputs $tr.find('.mountOptionsToggle, .remove').empty(); - $tr.find('input, select, button').attr('disabled', 'disabled'); + $tr.find('input:not(.user_provided), select:not(.user_provided)').attr('disabled', 'disabled'); + + if (isUserProvidedAuth) { + $tr.find('.configuration').find(':not(.user_provided)').remove(); + } else { + // userglobal storages do not expose configuration data + $tr.find('.configuration').text(t('files_external', 'Admin defined')); + } }); onCompletion.resolve(); } @@ -918,22 +974,40 @@ MountConfigListView.prototype = _.extend({ * @return {jQuery} newly created input */ writeParameterInput: function($td, parameter, placeholder, classes) { + var hasFlag = function(flag) { + return placeholder.indexOf(flag) !== -1; + }; classes = $.isArray(classes) ? classes : []; classes.push('added'); if (placeholder.indexOf('&') === 0) { classes.push('optional'); placeholder = placeholder.substring(1); } + + if (hasFlag('@')) { + if (this._isPersonal) { + classes.push('user_provided'); + } else { + return; + } + } + var newElement; - if (placeholder.indexOf('*') === 0) { - newElement = $(''); - } else if (placeholder.indexOf('!') === 0) { + + var trimmedPlaceholder = placeholder; + var flags = ['@', '*', '!', '#', '&']; + while(flags.indexOf(trimmedPlaceholder[0]) !== -1) { + trimmedPlaceholder = trimmedPlaceholder.substr(1); + } + if (hasFlag('*')) { + newElement = $(''); + } else if (hasFlag('!')) { var checkboxId = _.uniqueId('checkbox_'); - newElement = $(''); - } else if (placeholder.indexOf('#') === 0) { + newElement = $(''); + } else if (hasFlag('#')) { newElement = $(''); } else { - newElement = $(''); + newElement = $(''); } highlightInput(newElement); $td.append(newElement); @@ -952,7 +1026,12 @@ MountConfigListView.prototype = _.extend({ // new entry storageId = null; } - var storage = new this._storageConfigClass(storageId); + + var storage = $tr.data('storageConfig'); + if (!storage) { + storage = new this._storageConfigClass(storageId); + } + storage.errors = null; storage.mountPoint = $tr.find('.mountPoint input').val(); storage.backend = $tr.find('.backend').data('identifier'); storage.authMechanism = $tr.find('.selectAuthMechanism').val(); @@ -966,7 +1045,7 @@ MountConfigListView.prototype = _.extend({ if ($input.attr('type') === 'button') { return; } - if (!isInputValid($input)) { + if (!isInputValid($input) && !$input.hasClass('optional')) { missingOptions.push(parameter); return; } @@ -994,7 +1073,7 @@ MountConfigListView.prototype = _.extend({ var users = []; var multiselect = getSelection($tr); $.each(multiselect, function(index, value) { - var pos = value.indexOf('(group)'); + var pos = (value.indexOf)?value.indexOf('(group)'): -1; if (pos !== -1) { groups.push(value.substr(0, pos)); } else { @@ -1057,7 +1136,9 @@ MountConfigListView.prototype = _.extend({ saveStorageConfig:function($tr, callback, concurrentTimer) { var self = this; var storage = this.getStorageConfig($tr); - if (!storage.validate()) { + console.log(storage); + if (!storage || !storage.validate()) { + console.log('invalid'); return false; } diff --git a/apps/files_external/lib/auth/password/userprovided.php b/apps/files_external/lib/auth/password/userprovided.php index 8854513e16..b0ff50a279 100644 --- a/apps/files_external/lib/auth/password/userprovided.php +++ b/apps/files_external/lib/auth/password/userprovided.php @@ -21,6 +21,7 @@ namespace OCA\Files_External\Lib\Auth\Password; +use OCA\Files_External\Lib\DefinitionParameter; use OCP\IL10N; use OCP\IUser; use OCA\Files_External\Lib\Auth\AuthMechanism; @@ -46,7 +47,13 @@ class UserProvided extends AuthMechanism { ->setIdentifier('password::userprovided') ->setScheme(self::SCHEME_PASSWORD) ->setText($l->t('User provided')) - ->addParameters([]); + ->addParameters([ + (new DefinitionParameter('user', $l->t('Username'))) + ->setFlag(DefinitionParameter::FLAG_USER_PROVIDED), + (new DefinitionParameter('password', $l->t('Password'))) + ->setType(DefinitionParameter::VALUE_PASSWORD) + ->setFlag(DefinitionParameter::FLAG_USER_PROVIDED), + ]); } private function getCredentialsIdentifier($storageId) { diff --git a/apps/files_external/lib/definitionparameter.php b/apps/files_external/lib/definitionparameter.php index 59a098e632..85c3dd08ad 100644 --- a/apps/files_external/lib/definitionparameter.php +++ b/apps/files_external/lib/definitionparameter.php @@ -35,6 +35,7 @@ class DefinitionParameter implements \JsonSerializable { /** Flag constants */ const FLAG_NONE = 0; const FLAG_OPTIONAL = 1; + const FLAG_USER_PROVIDED = 2; /** @var string name of parameter */ private $name; @@ -121,7 +122,7 @@ class DefinitionParameter implements \JsonSerializable { * @return bool */ public function isFlagSet($flag) { - return (bool) $this->flags & $flag; + return (bool)($this->flags & $flag); } /** @@ -143,10 +144,11 @@ class DefinitionParameter implements \JsonSerializable { break; } - switch ($this->getFlags()) { - case self::FLAG_OPTIONAL: - $prefix = '&' . $prefix; - break; + if ($this->isFlagSet(self::FLAG_OPTIONAL)) { + $prefix = '&' . $prefix; + } + if ($this->isFlagSet(self::FLAG_USER_PROVIDED)) { + $prefix = '@' . $prefix; } return $prefix . $this->getText(); @@ -160,28 +162,28 @@ class DefinitionParameter implements \JsonSerializable { * @return bool success */ public function validateValue(&$value) { - $optional = $this->getFlags() & self::FLAG_OPTIONAL; + $optional = $this->isFlagSet(self::FLAG_OPTIONAL) || $this->isFlagSet(self::FLAG_USER_PROVIDED); switch ($this->getType()) { - case self::VALUE_BOOLEAN: - if (!is_bool($value)) { - switch ($value) { - case 'true': - $value = true; - break; - case 'false': - $value = false; - break; - default: + case self::VALUE_BOOLEAN: + if (!is_bool($value)) { + switch ($value) { + case 'true': + $value = true; + break; + case 'false': + $value = false; + break; + default: + return false; + } + } + break; + default: + if (!$value && !$optional) { return false; } - } - break; - default: - if (!$value && !$optional) { - return false; - } - break; + break; } return true; }