Propagate auth mechanism/backend failures to filesystem layer

Failure to prepare the storage during backend or auth mechanism
manipulation will throw an InsufficientDataForMeaningfulAnswerException,
which is propagated to StorageNotAvailableException in the filesystem
layer via the FailedStorage helper class.

When a storage is unavailable not due to failure, but due to
insufficient data being available, a special 'indeterminate' status is
returned to the configuration UI.
This commit is contained in:
Robin McCorkell 2015-08-12 19:51:09 +01:00
parent c592e24c87
commit b6eb952ac6
8 changed files with 297 additions and 27 deletions

View File

@ -34,6 +34,8 @@ use \OCA\Files_external\NotFoundException;
use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_External\Lib\Backend\Backend;
use \OCA\Files_External\Lib\Auth\AuthMechanism;
use \OCP\Files\StorageNotAvailableException;
use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
/**
* Base class for storages controllers
@ -182,21 +184,27 @@ abstract class StoragesController extends Controller {
* @param StorageConfig $storage storage configuration
*/
protected function updateStorageStatus(StorageConfig &$storage) {
/** @var AuthMechanism */
$authMechanism = $storage->getAuthMechanism();
$authMechanism->manipulateStorageConfig($storage);
/** @var Backend */
$backend = $storage->getBackend();
$backend->manipulateStorageConfig($storage);
try {
/** @var AuthMechanism */
$authMechanism = $storage->getAuthMechanism();
$authMechanism->manipulateStorageConfig($storage);
/** @var Backend */
$backend = $storage->getBackend();
$backend->manipulateStorageConfig($storage);
// update status (can be time-consuming)
$storage->setStatus(
\OC_Mount_Config::getBackendStatus(
$storage->getBackend()->getStorageClass(),
$storage->getBackendOptions(),
false
)
);
// update status (can be time-consuming)
$storage->setStatus(
\OC_Mount_Config::getBackendStatus(
$backend->getStorageClass(),
$storage->getBackendOptions(),
false
)
);
} catch (InsufficientDataForMeaningfulAnswerException $e) {
$storage->setStatus(\OC_Mount_Config::STATUS_INDETERMINATE);
} catch (StorageNotAvailableException $e) {
$storage->setStatus(\OC_Mount_Config::STATUS_ERROR);
}
}
/**

View File

@ -191,7 +191,8 @@ var StorageConfig = function(id) {
StorageConfig.Status = {
IN_PROGRESS: -1,
SUCCESS: 0,
ERROR: 1
ERROR: 1,
INDETERMINATE: 2
};
/**
* @memberof OCA.External.Settings
@ -946,7 +947,7 @@ MountConfigListView.prototype = {
*/
updateStatus: function($tr, status) {
var $statusSpan = $tr.find('.status span');
$statusSpan.removeClass('success error loading-small');
$statusSpan.removeClass('loading-small success indeterminate error');
switch (status) {
case StorageConfig.Status.IN_PROGRESS:
$statusSpan.addClass('loading-small');
@ -954,6 +955,9 @@ MountConfigListView.prototype = {
case StorageConfig.Status.SUCCESS:
$statusSpan.addClass('success');
break;
case StorageConfig.Status.INDETERMINATE:
$statusSpan.addClass('indeterminate');
break;
default:
$statusSpan.addClass('error');
}

View File

@ -52,6 +52,7 @@ class OC_Mount_Config {
// getBackendStatus return types
const STATUS_SUCCESS = 0;
const STATUS_ERROR = 1;
const STATUS_INDETERMINATE = 2;
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false;
@ -218,7 +219,7 @@ class OC_Mount_Config {
* @param string|array $input
* @return string
*/
private static function setUserVars($user, $input) {
public static function setUserVars($user, $input) {
if (is_array($input)) {
foreach ($input as &$value) {
if (is_string($value)) {

View File

@ -32,6 +32,8 @@ use OCP\IUser;
use OCA\Files_external\Service\UserStoragesService;
use OCA\Files_External\Service\UserGlobalStoragesService;
use OCA\Files_External\Lib\StorageConfig;
use OCP\Files\StorageNotAvailableException;
use OCA\Files_External\Lib\FailedStorage;
/**
* Make the old files_external config work with the new public mount config api
@ -60,8 +62,15 @@ class ConfigAdapter implements IMountProvider {
* Process storage ready for mounting
*
* @param StorageConfig $storage
* @param IUser $user
*/
private function prepareStorageConfig(StorageConfig &$storage) {
private function prepareStorageConfig(StorageConfig &$storage, IUser $user) {
foreach ($storage->getBackendOptions() as $option => $value) {
$storage->setBackendOption($option, \OC_Mount_Config::setUserVars(
$user->getUID(), $value
));
}
$objectStore = $storage->getBackendOption('objectstore');
if ($objectStore) {
$objectClass = $objectStore['class'];
@ -103,8 +112,13 @@ class ConfigAdapter implements IMountProvider {
$this->userGlobalStoragesService->setUser($user);
foreach ($this->userGlobalStoragesService->getAllStorages() as $storage) {
$this->prepareStorageConfig($storage);
$impl = $this->constructStorage($storage);
try {
$this->prepareStorageConfig($storage, $user);
$impl = $this->constructStorage($storage);
} catch (\Exception $e) {
// propagate exception into filesystem
$impl = new FailedStorage(['exception' => $e]);
}
$mount = new MountPoint(
$impl,
@ -117,8 +131,13 @@ class ConfigAdapter implements IMountProvider {
}
foreach ($this->userStoragesService->getAllStorages() as $storage) {
$this->prepareStorageConfig($storage);
$impl = $this->constructStorage($storage);
try {
$this->prepareStorageConfig($storage, $user);
$impl = $this->constructStorage($storage);
} catch (\Exception $e) {
// propagate exception into filesystem
$impl = new FailedStorage(['exception' => $e]);
}
$mount = new PersonalMount(
$this->userStoragesService,

View File

@ -0,0 +1,200 @@
<?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;
use \OCP\Lock\ILockingProvider;
use \OC\Files\Storage\Common;
use \OCP\Files\StorageNotAvailableException;
/**
* Storage placeholder to represent a missing precondition, storage unavailable
*/
class FailedStorage extends Common {
/** @var \Exception */
protected $e;
/**
* @param array $params ['exception' => \Exception]
*/
public function __construct($params) {
$this->e = $params['exception'];
}
public function getId() {
// we can't return anything sane here
return 'failedstorage';
}
public function mkdir($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function rmdir($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function opendir($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function is_dir($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function is_file($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function stat($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function filetype($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function filesize($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function isCreatable($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function isReadable($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function isUpdatable($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function isDeletable($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function isSharable($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getPermissions($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function file_exists($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function filemtime($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function file_get_contents($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function file_put_contents($path, $data) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function unlink($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function rename($path1, $path2) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function copy($path1, $path2) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function fopen($path, $mode) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getMimeType($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function hash($type, $path, $raw = false) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function free_space($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function search($query) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function touch($path, $mtime = null) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getLocalFile($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getLocalFolder($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function hasUpdated($path, $time) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getETag($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function getDirectDownload($path) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function verifyPath($path, $fileName) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function acquireLock($path, $type, ILockingProvider $provider) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function releaseLock($path, $type, ILockingProvider $provider) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
public function changeLock($path, $type, ILockingProvider $provider) {
throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e);
}
}

View File

@ -0,0 +1,30 @@
<?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;
use \OCP\Files\StorageNotAvailableException;
/**
* Authentication mechanism or backend has insufficient data
*/
class InsufficientDataForMeaningfulAnswerException extends StorageNotAvailableException {
}

View File

@ -23,6 +23,8 @@ namespace OCA\Files_External\Lib;
use \OCP\Files\Storage;
use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
use \OCP\Files\StorageNotAvailableException;
/**
* Trait for objects that can modify StorageConfigs and wrap Storages
@ -43,6 +45,8 @@ trait StorageModifierTrait {
* Modify a StorageConfig parameters
*
* @param StorageConfig $storage
* @throws InsufficientDataForMeaningfulAnswerException
* @throws StorageNotAvailableException
*/
public function manipulateStorageConfig(StorageConfig &$storage) {
}
@ -52,6 +56,8 @@ trait StorageModifierTrait {
*
* @param Storage $storage
* @return Storage
* @throws InsufficientDataForMeaningfulAnswerException
* @throws StorageNotAvailableException
*/
public function wrapStorage(Storage $storage) {
return $storage;

View File

@ -431,14 +431,16 @@ table.grid td.date{
}
span.success {
background: #37ce02;
border-radius: 3px;
background: #37ce02;
border-radius: 3px;
}
span.error {
background: #ce3702;
background: #ce3702;
}
span.indeterminate {
background: #e6db00;
border-radius: 40% 0;
}
/* PASSWORD */
.strengthify-wrapper {