nextcloud/apps/files_external/lib/Service/BackendService.php

370 lines
9.8 KiB
PHP

<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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\Service;
use OCA\Files_External\Config\IConfigHandler;
use OCA\Files_External\Lib\Auth\AuthMechanism;
use OCA\Files_External\Lib\Backend\Backend;
use OCA\Files_External\Lib\Config\IAuthMechanismProvider;
use OCA\Files_External\Lib\Config\IBackendProvider;
use OCP\EventDispatcher\GenericEvent;
use OCP\IConfig;
/**
* Service class to manage backend definitions
*/
class BackendService {
/** Visibility constants for VisibilityTrait */
public const VISIBILITY_NONE = 0;
public const VISIBILITY_PERSONAL = 1;
public const VISIBILITY_ADMIN = 2;
//const VISIBILITY_ALIENS = 4;
public const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN
/** Priority constants for PriorityTrait */
public const PRIORITY_DEFAULT = 100;
/** @var IConfig */
protected $config;
/** @var bool */
private $userMountingAllowed = true;
/** @var string[] */
private $userMountingBackends = [];
/** @var Backend[] */
private $backends = [];
/** @var IBackendProvider[] */
private $backendProviders = [];
/** @var AuthMechanism[] */
private $authMechanisms = [];
/** @var IAuthMechanismProvider[] */
private $authMechanismProviders = [];
/** @var callable[] */
private $configHandlerLoaders = [];
private $configHandlers = [];
/**
* @param IConfig $config
*/
public function __construct(
IConfig $config
) {
$this->config = $config;
// Load config values
if ($this->config->getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') {
$this->userMountingAllowed = false;
}
$this->userMountingBackends = explode(',',
$this->config->getAppValue('files_external', 'user_mounting_backends', '')
);
// if no backend is in the list an empty string is in the array and user mounting is disabled
if ($this->userMountingBackends === ['']) {
$this->userMountingAllowed = false;
}
}
/**
* Register a backend provider
*
* @since 9.1.0
* @param IBackendProvider $provider
*/
public function registerBackendProvider(IBackendProvider $provider) {
$this->backendProviders[] = $provider;
}
private function callForRegistrations() {
static $eventSent = false;
if (!$eventSent) {
\OC::$server->getEventDispatcher()->dispatch(
'OCA\\Files_External::loadAdditionalBackends',
new GenericEvent()
);
$eventSent = true;
}
}
private function loadBackendProviders() {
$this->callForRegistrations();
foreach ($this->backendProviders as $provider) {
$this->registerBackends($provider->getBackends());
}
$this->backendProviders = [];
}
/**
* Register an auth mechanism provider
*
* @since 9.1.0
* @param IAuthMechanismProvider $provider
*/
public function registerAuthMechanismProvider(IAuthMechanismProvider $provider) {
$this->authMechanismProviders[] = $provider;
}
private function loadAuthMechanismProviders() {
$this->callForRegistrations();
foreach ($this->authMechanismProviders as $provider) {
$this->registerAuthMechanisms($provider->getAuthMechanisms());
}
$this->authMechanismProviders = [];
}
/**
* Register a backend
*
* @deprecated 9.1.0 use registerBackendProvider()
* @param Backend $backend
*/
public function registerBackend(Backend $backend) {
if (!$this->isAllowedUserBackend($backend)) {
$backend->removeVisibility(BackendService::VISIBILITY_PERSONAL);
}
foreach ($backend->getIdentifierAliases() as $alias) {
$this->backends[$alias] = $backend;
}
}
/**
* @deprecated 9.1.0 use registerBackendProvider()
* @param Backend[] $backends
*/
public function registerBackends(array $backends) {
foreach ($backends as $backend) {
$this->registerBackend($backend);
}
}
/**
* Register an authentication mechanism
*
* @deprecated 9.1.0 use registerAuthMechanismProvider()
* @param AuthMechanism $authMech
*/
public function registerAuthMechanism(AuthMechanism $authMech) {
if (!$this->isAllowedAuthMechanism($authMech)) {
$authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL);
}
foreach ($authMech->getIdentifierAliases() as $alias) {
$this->authMechanisms[$alias] = $authMech;
}
}
/**
* @deprecated 9.1.0 use registerAuthMechanismProvider()
* @param AuthMechanism[] $mechanisms
*/
public function registerAuthMechanisms(array $mechanisms) {
foreach ($mechanisms as $mechanism) {
$this->registerAuthMechanism($mechanism);
}
}
/**
* Get all backends
*
* @return Backend[]
*/
public function getBackends() {
$this->loadBackendProviders();
// only return real identifiers, no aliases
$backends = [];
foreach ($this->backends as $backend) {
$backends[$backend->getIdentifier()] = $backend;
}
return $backends;
}
/**
* Get all available backends
*
* @return Backend[]
*/
public function getAvailableBackends() {
return array_filter($this->getBackends(), function ($backend) {
return !$backend->checkDependencies();
});
}
/**
* @param string $identifier
* @return Backend|null
*/
public function getBackend($identifier) {
$this->loadBackendProviders();
if (isset($this->backends[$identifier])) {
return $this->backends[$identifier];
}
return null;
}
/**
* Get all authentication mechanisms
*
* @return AuthMechanism[]
*/
public function getAuthMechanisms() {
$this->loadAuthMechanismProviders();
// only return real identifiers, no aliases
$mechanisms = [];
foreach ($this->authMechanisms as $mechanism) {
$mechanisms[$mechanism->getIdentifier()] = $mechanism;
}
return $mechanisms;
}
/**
* Get all authentication mechanisms for schemes
*
* @param string[] $schemes
* @return AuthMechanism[]
*/
public function getAuthMechanismsByScheme(array $schemes) {
return array_filter($this->getAuthMechanisms(), function ($authMech) use ($schemes) {
return in_array($authMech->getScheme(), $schemes, true);
});
}
/**
* @param string $identifier
* @return AuthMechanism|null
*/
public function getAuthMechanism($identifier) {
$this->loadAuthMechanismProviders();
if (isset($this->authMechanisms[$identifier])) {
return $this->authMechanisms[$identifier];
}
return null;
}
/**
* @return bool
*/
public function isUserMountingAllowed() {
return $this->userMountingAllowed;
}
/**
* Check a backend if a user is allowed to mount it
*
* @param Backend $backend
* @return bool
*/
protected function isAllowedUserBackend(Backend $backend) {
if ($this->userMountingAllowed &&
array_intersect($backend->getIdentifierAliases(), $this->userMountingBackends)
) {
return true;
}
return false;
}
/**
* Check an authentication mechanism if a user is allowed to use it
*
* @param AuthMechanism $authMechanism
* @return bool
*/
protected function isAllowedAuthMechanism(AuthMechanism $authMechanism) {
return true; // not implemented
}
/**
* registers a configuration handler
*
* The function of the provided $placeholder is mostly to act a sorting
* criteria, so longer placeholders are replaced first. This avoids
* "$user" overwriting parts of "$userMail" and "$userLang", for example.
* The provided value should not contain the $ prefix, only a-z0-9 are
* allowed. Upper case letters are lower cased, the replacement is case-
* insensitive.
*
* The configHandlerLoader should just instantiate the handler on demand.
* For now all handlers are instantiated when a mount is loaded, independent
* of whether the placeholder is present or not. This may change in future.
*
* @since 16.0.0
*/
public function registerConfigHandler(string $placeholder, callable $configHandlerLoader) {
$placeholder = trim(strtolower($placeholder));
if (!(bool)\preg_match('/^[a-z0-9]*$/', $placeholder)) {
throw new \RuntimeException(sprintf(
'Invalid placeholder %s, only [a-z0-9] are allowed', $placeholder
));
}
if ($placeholder === '') {
throw new \RuntimeException('Invalid empty placeholder');
}
if (isset($this->configHandlerLoaders[$placeholder]) || isset($this->configHandlers[$placeholder])) {
throw new \RuntimeException(sprintf('A handler is already registered for %s', $placeholder));
}
$this->configHandlerLoaders[$placeholder] = $configHandlerLoader;
}
protected function loadConfigHandlers():void {
$this->callForRegistrations();
$newLoaded = false;
foreach ($this->configHandlerLoaders as $placeholder => $loader) {
$handler = $loader();
if (!$handler instanceof IConfigHandler) {
throw new \RuntimeException(sprintf(
'Handler for %s is not an instance of IConfigHandler', $placeholder
));
}
$this->configHandlers[$placeholder] = $handler;
$newLoaded = true;
}
$this->configHandlerLoaders = [];
if ($newLoaded) {
// ensure those with longest placeholders come first,
// to avoid substring matches
uksort($this->configHandlers, function ($phA, $phB) {
return strlen($phB) <=> strlen($phA);
});
}
}
/**
* @since 16.0.0
*/
public function getConfigHandlers() {
$this->loadConfigHandlers();
return $this->configHandlers;
}
}