summaryrefslogtreecommitdiffstats
path: root/apps/files_external/lib
diff options
context:
space:
mode:
authorRobin McCorkell <rmccorkell@owncloud.com>2015-08-12 10:54:03 +0100
committerRobin McCorkell <rmccorkell@owncloud.com>2015-08-19 10:05:11 +0100
commit272a46ebe1a5e195a078dde74f5f2ad941923d9e (patch)
tree58ea576ad6362e465526c65e7a7cae14e5df7013 /apps/files_external/lib
parenta6a69ef1dfe1545e1362953803219ed6f28f71a5 (diff)
downloadnextcloud-server-272a46ebe1a5e195a078dde74f5f2ad941923d9e.tar.gz
nextcloud-server-272a46ebe1a5e195a078dde74f5f2ad941923d9e.zip
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it supports, instead of specifying the parameters for its authentication method directly. This allows multiple authentication mechanisms to be implemented for a single scheme, providing altered functionality. This commit introduces the backend framework for this feature, and so at this point the UI will be broken as the frontend does not specify the required information. Terminology: - authentication scheme Parameter interface for the authentication method. A backend supporting the 'password' scheme accepts two parameters, 'user' and 'password'. - authentication mechanism Specific mechanism implementing a scheme. Basic mechanisms may forward configuration options directly to the backend, more advanced ones may lookup parameters or retrieve them from the session New dropdown selector for external storage configurations to select the authentication mechanism to be used. Authentication mechanisms can have visibilities, just like backends. The API was extended too to make it easier to add/remove visibilities. In addition, the concept of 'allowed visibility' has been introduced, so a backend/auth mechanism can force a maximum visibility level (e.g. Local storage type) that cannot be overridden by configuration in the web UI. An authentication mechanism is a fully instantiated implementation. This allows an implementation to have dependencies injected into it, e.g. an \OCP\IDB for database operations. When a StorageConfig is being prepared for mounting, the authentication mechanism implementation has manipulateStorage() called, which inserts the relevant authentication method options into the storage ready for mounting.
Diffstat (limited to 'apps/files_external/lib')
-rw-r--r--apps/files_external/lib/auth/authmechanism.php120
-rw-r--r--apps/files_external/lib/auth/nullmechanism.php40
-rw-r--r--apps/files_external/lib/backend/backend.php76
-rw-r--r--apps/files_external/lib/config.php16
-rw-r--r--apps/files_external/lib/config/configadapter.php3
-rw-r--r--apps/files_external/lib/storageconfig.php23
-rw-r--r--apps/files_external/lib/storagemodifiertrait.php10
-rw-r--r--apps/files_external/lib/visibilitytrait.php7
8 files changed, 294 insertions, 1 deletions
diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php
new file mode 100644
index 00000000000..7da57662db8
--- /dev/null
+++ b/apps/files_external/lib/auth/authmechanism.php
@@ -0,0 +1,120 @@
+<?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;
+
+use \OCA\Files_External\Lib\StorageConfig;
+use \OCA\Files_External\Lib\VisibilityTrait;
+use \OCA\Files_External\Lib\FrontendDefinitionTrait;
+use \OCA\Files_External\Lib\StorageModifierTrait;
+
+/**
+ * Authentication mechanism
+ *
+ * An authentication mechanism can have services injected during construction,
+ * such as \OCP\IDB for database operations. This allows an authentication
+ * mechanism to perform advanced operations based on provided information.
+ *
+ * An authenication scheme defines the parameter interface, common to the
+ * storage implementation, the backend and the authentication mechanism.
+ * A storage implementation expects parameters according to the authentication
+ * scheme, which are provided from the authentication mechanism.
+ *
+ * This class uses the following traits:
+ * - VisibilityTrait
+ * Restrict usage to admin-only/none
+ * - FrontendDefinitionTrait
+ * Specify configuration parameters and other definitions
+ * - StorageModifierTrait
+ * Object can affect storage mounting
+ */
+class AuthMechanism implements \JsonSerializable {
+
+ /** Standard authentication schemes */
+ const SCHEME_NULL = 'null';
+ const SCHEME_PASSWORD = 'password';
+ const SCHEME_OAUTH1 = 'oauth1';
+ const SCHEME_OAUTH2 = 'oauth2';
+ const SCHEME_PUBLICKEY = 'publickey';
+ const SCHEME_OPENSTACK = 'openstack';
+
+ use VisibilityTrait;
+ use FrontendDefinitionTrait;
+ use StorageModifierTrait;
+
+ /** @var string */
+ protected $scheme;
+
+ /**
+ * @return string
+ */
+ public function getClass() {
+ return '\\'.get_class($this);
+ }
+
+ /**
+ * Get the authentication scheme implemented
+ * See self::SCHEME_* constants
+ *
+ * @return string
+ */
+ public function getScheme() {
+ return $this->scheme;
+ }
+
+ /**
+ * @param string $scheme
+ * @return self
+ */
+ public function setScheme($scheme) {
+ $this->scheme = $scheme;
+ return $this;
+ }
+
+ /**
+ * Serialize into JSON for client-side JS
+ *
+ * @return array
+ */
+ public function jsonSerialize() {
+ $data = $this->jsonSerializeDefinition();
+ $data['scheme'] = $this->getScheme();
+
+ return $data;
+ }
+
+ /**
+ * Check if parameters are satisfied in a StorageConfig
+ *
+ * @param StorageConfig $storage
+ * @return bool
+ */
+ public function validateStorage(StorageConfig $storage) {
+ // does the backend actually support this scheme
+ $supportedSchemes = $storage->getBackend()->getAuthSchemes();
+ if (!isset($supportedSchemes[$this->getScheme()])) {
+ return false;
+ }
+
+ return $this->validateStorageDefinition($storage);
+ }
+
+}
diff --git a/apps/files_external/lib/auth/nullmechanism.php b/apps/files_external/lib/auth/nullmechanism.php
new file mode 100644
index 00000000000..396649d7319
--- /dev/null
+++ b/apps/files_external/lib/auth/nullmechanism.php
@@ -0,0 +1,40 @@
+<?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;
+
+use \OCP\IL10N;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
+use \OCA\Files_external\Lib\StorageConfig;
+
+/**
+ * Null authentication mechanism
+ */
+class NullMechanism extends AuthMechanism {
+
+ public function __construct(IL10N $l) {
+ $this
+ ->setScheme(self::SCHEME_NULL)
+ ->setText($l->t('None'))
+ ;
+ }
+
+}
diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php
index e7cd27a1d6c..634bcb7bfbd 100644
--- a/apps/files_external/lib/backend/backend.php
+++ b/apps/files_external/lib/backend/backend.php
@@ -27,9 +27,31 @@ use \OCA\Files_External\Lib\FrontendDefinitionTrait;
use \OCA\Files_External\Lib\PriorityTrait;
use \OCA\Files_External\Lib\DependencyTrait;
use \OCA\Files_External\Lib\StorageModifierTrait;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
/**
* Storage backend
+ *
+ * A backend can have services injected during construction,
+ * such as \OCP\IDB for database operations. This allows a backend
+ * to perform advanced operations based on provided information.
+ *
+ * An authenication scheme defines the parameter interface, common to the
+ * storage implementation, the backend and the authentication mechanism.
+ * A storage implementation expects parameters according to the authentication
+ * scheme, which are provided from the authentication mechanism.
+ *
+ * This class uses the following traits:
+ * - VisibilityTrait
+ * Restrict usage to admin-only/none
+ * - FrontendDefinitionTrait
+ * Specify configuration parameters and other definitions
+ * - PriorityTrait
+ * Allow objects to prioritize over others with the same mountpoint
+ * - DependencyTrait
+ * The object requires certain dependencies to be met
+ * - StorageModifierTrait
+ * Object can affect storage mounting
*/
class Backend implements \JsonSerializable {
@@ -42,6 +64,12 @@ class Backend implements \JsonSerializable {
/** @var string storage class */
private $storageClass;
+ /** @var array 'scheme' => true, supported authentication schemes */
+ private $authSchemes = [];
+
+ /** @var AuthMechanism|callable authentication mechanism fallback */
+ private $legacyAuthMechanism;
+
/**
* @return string
*/
@@ -67,6 +95,53 @@ class Backend implements \JsonSerializable {
}
/**
+ * @return array
+ */
+ public function getAuthSchemes() {
+ if (empty($this->authSchemes)) {
+ return [AuthMechanism::SCHEME_NULL => true];
+ }
+ return $this->authSchemes;
+ }
+
+ /**
+ * @param string $scheme
+ * @return self
+ */
+ public function addAuthScheme($scheme) {
+ $this->authSchemes[$scheme] = true;
+ return $this;
+ }
+
+ /**
+ * @param array $parameters storage parameters, for dynamic mechanism selection
+ * @return AuthMechanism
+ */
+ public function getLegacyAuthMechanism(array $parameters = []) {
+ if (is_callable($this->legacyAuthMechanism)) {
+ return call_user_func($this->legacyAuthMechanism, $parameters);
+ }
+ return $this->legacyAuthMechanism;
+ }
+
+ /**
+ * @param AuthMechanism $authMechanism
+ * @return self
+ */
+ public function setLegacyAuthMechanism(AuthMechanism $authMechanism) {
+ $this->legacyAuthMechanism = $authMechanism;
+ return $this;
+ }
+
+ /**
+ * @param callable $callback dynamic auth mechanism selection
+ * @return self
+ */
+ public function setLegacyAuthMechanismCallback(callable $callback) {
+ $this->legacyAuthMechanism = $callback;
+ }
+
+ /**
* Serialize into JSON for client-side JS
*
* @return array
@@ -76,6 +151,7 @@ class Backend implements \JsonSerializable {
$data['backend'] = $data['name']; // legacy compat
$data['priority'] = $this->getPriority();
+ $data['authSchemes'] = $this->getAuthSchemes();
return $data;
}
diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php
index 11dec94621a..e720677480f 100644
--- a/apps/files_external/lib/config.php
+++ b/apps/files_external/lib/config.php
@@ -123,7 +123,9 @@ class OC_Mount_Config {
if (!isset($options['priority'])) {
$options['priority'] = $backend->getPriority();
}
-
+ if (!isset($options['authMechanism'])) {
+ $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass();
+ }
// Override if priority greater
if ((!isset($mountPoints[$mountPoint]))
@@ -149,6 +151,9 @@ class OC_Mount_Config {
if (!isset($options['priority'])) {
$options['priority'] = $backend->getPriority();
}
+ if (!isset($options['authMechanism'])) {
+ $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass();
+ }
// Override if priority greater
if ((!isset($mountPoints[$mountPoint]))
@@ -175,6 +180,9 @@ class OC_Mount_Config {
if (!isset($options['priority'])) {
$options['priority'] = $backend->getPriority();
}
+ if (!isset($options['authMechanism'])) {
+ $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass();
+ }
// Override if priority greater or if priority type different
if ((!isset($mountPoints[$mountPoint]))
@@ -204,6 +212,9 @@ class OC_Mount_Config {
if (!isset($options['priority'])) {
$options['priority'] = $backend->getPriority();
}
+ if (!isset($options['authMechanism'])) {
+ $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass();
+ }
// Override if priority greater or if priority type different
if ((!isset($mountPoints[$mountPoint]))
@@ -227,6 +238,9 @@ class OC_Mount_Config {
if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) {
$options['personal'] = true;
$options['options'] = self::decryptPasswords($options['options']);
+ if (!isset($options['authMechanism'])) {
+ $options['authMechanism'] = $backend->getLegacyAuthMechanism($options['options'])->getClass();
+ }
// Always override previous config
$options['priority_type'] = self::MOUNT_TYPE_PERSONAL;
diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php
index 63615e716ae..9829629761c 100644
--- a/apps/files_external/lib/config/configadapter.php
+++ b/apps/files_external/lib/config/configadapter.php
@@ -68,6 +68,7 @@ class ConfigAdapter implements IMountProvider {
$storage->setBackendOption('objectstore', new $objectClass($objectStore));
}
+ $storage->getAuthMechanism()->manipulateStorageConfig($storage);
$storage->getBackend()->manipulateStorageConfig($storage);
}
@@ -81,7 +82,9 @@ class ConfigAdapter implements IMountProvider {
$class = $storageConfig->getBackend()->getStorageClass();
$storage = new $class($storageConfig->getBackendOptions());
+ // auth mechanism should fire first
$storage = $storageConfig->getBackend()->wrapStorage($storage);
+ $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
return $storage;
}
diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php
index cf8271ff4eb..96ba2f72ae6 100644
--- a/apps/files_external/lib/storageconfig.php
+++ b/apps/files_external/lib/storageconfig.php
@@ -22,6 +22,7 @@
namespace OCA\Files_external\Lib;
use \OCA\Files_External\Lib\Backend\Backend;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
/**
* External storage configuration
@@ -43,6 +44,13 @@ class StorageConfig implements \JsonSerializable {
private $backend;
/**
+ * Authentication mechanism
+ *
+ * @var AuthMechanism
+ */
+ private $authMechanism;
+
+ /**
* Backend options
*
* @var array
@@ -154,6 +162,20 @@ class StorageConfig implements \JsonSerializable {
}
/**
+ * @return AuthMechanism
+ */
+ public function getAuthMechanism() {
+ return $this->authMechanism;
+ }
+
+ /**
+ * @param AuthMechanism
+ */
+ public function setAuthMechanism(AuthMechanism $authMechanism) {
+ $this->authMechanism = $authMechanism;
+ }
+
+ /**
* Returns the external storage backend-specific options
*
* @return array backend options
@@ -301,6 +323,7 @@ class StorageConfig implements \JsonSerializable {
}
$result['mountPoint'] = $this->mountPoint;
$result['backendClass'] = $this->backend->getClass();
+ $result['authMechanismClass'] = $this->authMechanism->getClass();
$result['backendOptions'] = $this->backendOptions;
if (!is_null($this->priority)) {
$result['priority'] = $this->priority;
diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php
index f78116103db..a11d265e841 100644
--- a/apps/files_external/lib/storagemodifiertrait.php
+++ b/apps/files_external/lib/storagemodifiertrait.php
@@ -26,6 +26,16 @@ use \OCA\Files_External\Lib\StorageConfig;
/**
* Trait for objects that can modify StorageConfigs and wrap Storages
+ *
+ * When a storage implementation is being prepared for use, the StorageConfig
+ * is passed through manipulateStorageConfig() to update any parameters as
+ * necessary. After the storage implementation has been constructed, it is
+ * passed through wrapStorage(), potentially replacing the implementation with
+ * a wrapped storage that changes its behaviour.
+ *
+ * Certain configuration options need to be set before the implementation is
+ * constructed, while others are retrieved directly from the storage
+ * implementation and so need a wrapper to be modified.
*/
trait StorageModifierTrait {
diff --git a/apps/files_external/lib/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php
index 06c95dd70c9..dfd2d323ca6 100644
--- a/apps/files_external/lib/visibilitytrait.php
+++ b/apps/files_external/lib/visibilitytrait.php
@@ -25,6 +25,13 @@ 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 {