Allow setting user provided credentials from the personal settings page

This commit is contained in:
Robin Appelman 2016-01-19 14:16:11 +01:00
parent f3e9729a5f
commit 860d51487b
6 changed files with 180 additions and 44 deletions

View File

@ -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')

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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 = $('<input type="password" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />');
} else if (placeholder.indexOf('!') === 0) {
var trimmedPlaceholder = placeholder;
var flags = ['@', '*', '!', '#', '&'];
while(flags.indexOf(trimmedPlaceholder[0]) !== -1) {
trimmedPlaceholder = trimmedPlaceholder.substr(1);
}
if (hasFlag('*')) {
newElement = $('<input type="password" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
} else if (hasFlag('!')) {
var checkboxId = _.uniqueId('checkbox_');
newElement = $('<input type="checkbox" id="'+checkboxId+'" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" /><label for="'+checkboxId+'">'+placeholder.substring(1)+'</label>');
} else if (placeholder.indexOf('#') === 0) {
newElement = $('<input type="checkbox" id="'+checkboxId+'" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" /><label for="'+checkboxId+'">'+ trimmedPlaceholder+'</label>');
} else if (hasFlag('#')) {
newElement = $('<input type="hidden" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />');
} else {
newElement = $('<input type="text" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+placeholder+'" />');
newElement = $('<input type="text" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
}
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;
}

View File

@ -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) {

View File

@ -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:
if ($this->isFlagSet(self::FLAG_OPTIONAL)) {
$prefix = '&' . $prefix;
break;
}
if ($this->isFlagSet(self::FLAG_USER_PROVIDED)) {
$prefix = '@' . $prefix;
}
return $prefix . $this->getText();
@ -160,7 +162,7 @@ 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: