diff options
author | Robin Appelman <icewind@owncloud.com> | 2015-11-02 13:13:06 +0100 |
---|---|---|
committer | Vincent Petry <pvince81@owncloud.com> | 2015-12-04 13:48:21 +0100 |
commit | a1898dc2bf9a89def29c1437903e560609f0cf40 (patch) | |
tree | 527d941bd240dd1c02760819724d21dfdd5d041c /apps/files_external/service/storagesservice.php | |
parent | 98bb8372f7f0ab1f669cdd92d439814e1b6aaa1a (diff) | |
download | nextcloud-server-a1898dc2bf9a89def29c1437903e560609f0cf40.tar.gz nextcloud-server-a1898dc2bf9a89def29c1437903e560609f0cf40.zip |
db config backend for files_external
Diffstat (limited to 'apps/files_external/service/storagesservice.php')
-rw-r--r-- | apps/files_external/service/storagesservice.php | 444 |
1 files changed, 139 insertions, 305 deletions
diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index c847930ba2d..9be6498435c 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -42,88 +42,60 @@ abstract class StoragesService { protected $backendService; /** - * @param BackendService $backendService + * @var DBConfigService */ - public function __construct(BackendService $backendService) { - $this->backendService = $backendService; - } + protected $dbConfig; /** - * Read legacy config data - * - * @return array list of mount configs + * @param BackendService $backendService + * @param DBConfigService $dbConfigService */ - protected function readLegacyConfig() { - // read global config - return \OC_Mount_Config::readData(); + public function __construct(BackendService $backendService, DBConfigService $dbConfigService) { + $this->backendService = $backendService; + $this->dbConfig = $dbConfigService; } - /** - * Write legacy config data - * - * @param array $mountPoints - */ - protected function writeLegacyConfig(array $mountPoints) { - // write global config - \OC_Mount_Config::writeData(null, $mountPoints); + protected function readDBConfig() { + return $this->dbConfig->getAdminMounts(); } - /** - * Copy legacy storage options into the given storage config object. - * - * @param StorageConfig $storageConfig storage config to populate - * @param string $mountType mount type - * @param string $applicable applicable user or group - * @param array $storageOptions legacy storage options - * - * @return StorageConfig populated storage config - */ - protected function populateStorageConfigWithLegacyOptions( - &$storageConfig, - $mountType, - $applicable, - $storageOptions - ) { - $backend = $this->backendService->getBackend($storageOptions['backend']); - if (!$backend) { - throw new \UnexpectedValueException('Invalid backend '.$storageOptions['backend']); - } - $storageConfig->setBackend($backend); - - if (isset($storageOptions['authMechanism']) && $storageOptions['authMechanism'] !== 'builtin::builtin') { - $authMechanism = $this->backendService->getAuthMechanism($storageOptions['authMechanism']); - } else { - $authMechanism = $backend->getLegacyAuthMechanism($storageOptions); - $storageOptions['authMechanism'] = 'null'; // to make error handling easier - } - if (!$authMechanism) { - throw new \UnexpectedValueException('Invalid authentication mechanism '.$storageOptions['authMechanism']); - } - $storageConfig->setAuthMechanism($authMechanism); - - $storageConfig->setBackendOptions($storageOptions['options']); - if (isset($storageOptions['mountOptions'])) { - $storageConfig->setMountOptions($storageOptions['mountOptions']); - } - if (!isset($storageOptions['priority'])) { - $storageOptions['priority'] = $backend->getPriority(); - } - $storageConfig->setPriority($storageOptions['priority']); + protected function getStorageConfigFromDBMount(array $mount) { + $applicableUsers = array_filter($mount['applicable'], function ($applicable) { + return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER; + }); + $applicableUsers = array_map(function ($applicable) { + return $applicable['value']; + }, $applicableUsers); + + $applicableGroups = array_filter($mount['applicable'], function ($applicable) { + return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP; + }); + $applicableGroups = array_map(function ($applicable) { + return $applicable['value']; + }, $applicableGroups); - if ($mountType === \OC_Mount_Config::MOUNT_TYPE_USER) { - $applicableUsers = $storageConfig->getApplicableUsers(); - if ($applicable !== 'all') { - $applicableUsers[] = $applicable; - $storageConfig->setApplicableUsers($applicableUsers); - } - } else if ($mountType === \OC_Mount_Config::MOUNT_TYPE_GROUP) { - $applicableGroups = $storageConfig->getApplicableGroups(); - $applicableGroups[] = $applicable; - $storageConfig->setApplicableGroups($applicableGroups); + try { + $config = $this->createStorage( + $mount['mount_point'], + $mount['storage_backend'], + $mount['auth_backend'], + $mount['config'], + $mount['options'], + array_values($applicableUsers), + array_values($applicableGroups), + $mount['priority'] + ); + $config->setId((int)$mount['mount_id']); + return $config; + } catch (\UnexpectedValueException $e) { + // dont die if a storage backend doesn't exist + \OCP\Util::writeLog( + 'files_external', + 'Could not load storage: "' . $e->getMessage() . '"', + \OCP\Util::ERROR + ); + return null; } - - - return $storageConfig; } /** @@ -132,199 +104,20 @@ abstract class StoragesService { * @return array map of storage id to storage config */ protected function readConfig() { - $mountPoints = $this->readLegacyConfig(); - - /** - * Here is the how the horribly messy mount point array looks like - * from the mount.json file: - * - * $storageOptions = $mountPoints[$mountType][$applicable][$mountPath] - * - * - $mountType is either "user" or "group" - * - $applicable is the name of a user or group (or the current user for personal mounts) - * - $mountPath is the mount point path (where the storage must be mounted) - * - $storageOptions is a map of storage options: - * - "priority": storage priority - * - "backend": backend identifier - * - "class": LEGACY backend class name - * - "options": backend-specific options - * - "authMechanism": authentication mechanism identifier - * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc) - */ - - // group by storage id - $storages = []; - - // for storages without id (legacy), group by config hash for - // later processing - $storagesWithConfigHash = []; - - foreach ($mountPoints as $mountType => $applicables) { - foreach ($applicables as $applicable => $mountPaths) { - foreach ($mountPaths as $rootMountPath => $storageOptions) { - $currentStorage = null; - - /** - * Flag whether the config that was read already has an id. - * If not, it will use a config hash instead and generate - * a proper id later - * - * @var boolean - */ - $hasId = false; - - // the root mount point is in the format "/$user/files/the/mount/point" - // we remove the "/$user/files" prefix - $parts = explode('/', ltrim($rootMountPath, '/'), 3); - if (count($parts) < 3) { - // something went wrong, skip - \OCP\Util::writeLog( - 'files_external', - 'Could not parse mount point "' . $rootMountPath . '"', - \OCP\Util::ERROR - ); - continue; - } - - $relativeMountPath = rtrim($parts[2], '/'); - - // note: we cannot do this after the loop because the decrypted config - // options might be needed for the config hash - $storageOptions['options'] = \OC_Mount_Config::decryptPasswords($storageOptions['options']); - - if (!isset($storageOptions['backend'])) { - $storageOptions['backend'] = $storageOptions['class']; // legacy compat - } - if (!isset($storageOptions['authMechanism'])) { - $storageOptions['authMechanism'] = null; // ensure config hash works - } - - if (isset($storageOptions['id'])) { - $configId = (int)$storageOptions['id']; - if (isset($storages[$configId])) { - $currentStorage = $storages[$configId]; - } - $hasId = true; - } else { - // missing id in legacy config, need to generate - // but at this point we don't know the max-id, so use - // first group it by config hash - $storageOptions['mountpoint'] = $rootMountPath; - $configId = \OC_Mount_Config::makeConfigHash($storageOptions); - if (isset($storagesWithConfigHash[$configId])) { - $currentStorage = $storagesWithConfigHash[$configId]; - } - } - - if (is_null($currentStorage)) { - // create new - $currentStorage = new StorageConfig($configId); - $currentStorage->setMountPoint($relativeMountPath); - } - - try { - $this->populateStorageConfigWithLegacyOptions( - $currentStorage, - $mountType, - $applicable, - $storageOptions - ); - - if ($hasId) { - $storages[$configId] = $currentStorage; - } else { - $storagesWithConfigHash[$configId] = $currentStorage; - } - } catch (\UnexpectedValueException $e) { - // dont die if a storage backend doesn't exist - \OCP\Util::writeLog( - 'files_external', - 'Could not load storage: "' . $e->getMessage() . '"', - \OCP\Util::ERROR - ); - } - } - } - } - - // process storages with config hash, they must get a real id - if (!empty($storagesWithConfigHash)) { - $this->setRealStorageIds($storages, $storagesWithConfigHash); - } + $mounts = $this->readDBConfig(); + $configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts); + $configs = array_filter($configs, function ($config) { + return $config instanceof StorageConfig; + }); - // convert parameter values - foreach ($storages as $storage) { - $storage->getBackend()->validateStorageDefinition($storage); - $storage->getAuthMechanism()->validateStorageDefinition($storage); - } + $keys = array_map(function (StorageConfig $config) { + return $config->getId(); + }, $configs); - return $storages; + return array_combine($keys, $configs); } /** - * Replace config hash ID with real IDs, for migrating legacy storages - * - * @param StorageConfig[] $storages Storages with real IDs - * @param StorageConfig[] $storagesWithConfigHash Storages with config hash IDs - */ - protected function setRealStorageIds(array &$storages, array $storagesWithConfigHash) { - $nextId = $this->generateNextId($storages); - foreach ($storagesWithConfigHash as $storage) { - $storage->setId($nextId); - $storages[$nextId] = $storage; - $nextId++; - } - - // re-save the config with the generated ids - $this->writeConfig($storages); - } - - /** - * Add mount point into the messy mount point structure - * - * @param array $mountPoints messy array of mount points - * @param string $mountType mount type - * @param string $applicable single applicable user or group - * @param string $rootMountPoint root mount point to use - * @param array $storageConfig storage config to set to the mount point - */ - protected function addMountPoint(&$mountPoints, $mountType, $applicable, $rootMountPoint, $storageConfig) { - if (!isset($mountPoints[$mountType])) { - $mountPoints[$mountType] = []; - } - - if (!isset($mountPoints[$mountType][$applicable])) { - $mountPoints[$mountType][$applicable] = []; - } - - $options = [ - 'id' => $storageConfig->getId(), - 'backend' => $storageConfig->getBackend()->getIdentifier(), - //'class' => $storageConfig->getBackend()->getClass(), - 'authMechanism' => $storageConfig->getAuthMechanism()->getIdentifier(), - 'options' => $storageConfig->getBackendOptions(), - ]; - - if (!is_null($storageConfig->getPriority())) { - $options['priority'] = $storageConfig->getPriority(); - } - - $mountOptions = $storageConfig->getMountOptions(); - if (!empty($mountOptions)) { - $options['mountOptions'] = $mountOptions; - } - - $mountPoints[$mountType][$applicable][$rootMountPoint] = $options; - } - - /** - * Write the storages to the configuration. - * - * @param array $storages map of storage id to storage config - */ - abstract protected function writeConfig($storages); - - /** * Get a storage with status * * @param int $id storage id @@ -333,19 +126,19 @@ abstract class StoragesService { * @throws NotFoundException if the storage with the given id was not found */ public function getStorage($id) { - $allStorages = $this->readConfig(); + $mount = $this->dbConfig->getMountById($id); - if (!isset($allStorages[$id])) { + if (!is_array($mount)) { throw new NotFoundException('Storage with id "' . $id . '" not found'); } - return $allStorages[$id]; + return $this->getStorageConfigFromDBMount($mount); } /** * Gets all storages, valid or not * - * @return array array of storage configs + * @return StorageConfig[] array of storage configs */ public function getAllStorages() { return $this->readConfig(); @@ -354,7 +147,7 @@ abstract class StoragesService { /** * Gets all valid storages * - * @return array + * @return StorageConfig[] */ public function getStorages() { return array_filter($this->getAllStorages(), [$this, 'validateStorage']); @@ -392,24 +185,50 @@ abstract class StoragesService { */ abstract public function getVisibilityType(); + protected function getType() { + return DBConfigService::MOUNT_TYPE_ADMIN; + } + /** * Add new storage to the configuration * - * @param array $newStorage storage attributes + * @param StorageConfig $newStorage storage attributes * * @return StorageConfig storage config, with added id */ public function addStorage(StorageConfig $newStorage) { $allStorages = $this->readConfig(); - $configId = $this->generateNextId($allStorages); + $configId = $this->dbConfig->addMount( + $newStorage->getMountPoint(), + $newStorage->getBackend()->getIdentifier(), + $newStorage->getAuthMechanism()->getIdentifier(), + $newStorage->getPriority(), + $this->getType() + ); + $newStorage->setId($configId); + foreach ($newStorage->getApplicableUsers() as $user) { + $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_USER, $user); + } + foreach ($newStorage->getApplicableGroups() as $group) { + $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GROUP, $group); + } + foreach ($newStorage->getBackendOptions() as $key => $value) { + $this->dbConfig->setConfig($configId, $key, $value); + } + foreach ($newStorage->getMountOptions() as $key => $value) { + $this->dbConfig->setOption($configId, $key, $value); + } + + if (count($newStorage->getApplicableUsers()) === 0 && count($newStorage->getApplicableGroups()) === 0) { + $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + } + // add new storage $allStorages[$configId] = $newStorage; - $this->writeConfig($allStorages); - $this->triggerHooks($newStorage, Filesystem::signal_create_mount); $newStorage->setStatus(StorageNotAvailableException::STATUS_SUCCESS); @@ -442,11 +261,11 @@ abstract class StoragesService { ) { $backend = $this->backendService->getBackend($backendIdentifier); if (!$backend) { - throw new \InvalidArgumentException('Unable to get backend for '.$backendIdentifier); + throw new \InvalidArgumentException('Unable to get backend for ' . $backendIdentifier); } $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier); if (!$authMechanism) { - throw new \InvalidArgumentException('Unable to get authentication mechanism for '.$authMechanismIdentifier); + throw new \InvalidArgumentException('Unable to get authentication mechanism for ' . $authMechanismIdentifier); } $newStorage = new StorageConfig(); $newStorage->setMountPoint($mountPoint); @@ -519,21 +338,56 @@ abstract class StoragesService { * @throws NotFoundException if the given storage does not exist in the config */ public function updateStorage(StorageConfig $updatedStorage) { - $allStorages = $this->readConfig(); - $id = $updatedStorage->getId(); - if (!isset($allStorages[$id])) { - throw new NotFoundException('Storage with id "' . $id . '" not found'); + + $existingMount = $this->dbConfig->getMountById($id); + + if (!is_array($existingMount)) { + throw new NotFoundException('Storage with id "' . $id . '" not found while updating storage'); + } + + $oldStorage = $this->getStorageConfigFromDBMount($existingMount); + + $removedUsers = array_diff($oldStorage->getApplicableUsers(), $updatedStorage->getApplicableUsers()); + $removedGroups = array_diff($oldStorage->getApplicableGroups(), $updatedStorage->getApplicableGroups()); + $addedUsers = array_diff($updatedStorage->getApplicableUsers(), $oldStorage->getApplicableUsers()); + $addedGroups = array_diff($updatedStorage->getApplicableGroups(), $oldStorage->getApplicableGroups()); + + $oldUserCount = count($oldStorage->getApplicableUsers()); + $oldGroupCount = count($oldStorage->getApplicableGroups()); + $newUserCount = count($oldStorage->getApplicableUsers()); + $newGroupCount = count($oldStorage->getApplicableGroups()); + $wasGlobal = ($oldUserCount + $oldGroupCount) === 0; + $isGlobal = ($newUserCount + $newGroupCount) === 0; + + foreach ($removedUsers as $user) { + $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user); + } + foreach ($removedGroups as $group) { + $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group); + } + foreach ($addedUsers as $user) { + $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user); + } + foreach ($addedGroups as $group) { + $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group); } - $oldStorage = $allStorages[$id]; - // ensure objectstore is persistent - if ($objectstore = $oldStorage->getBackendOption('objectstore')) { - $updatedStorage->setBackendOption('objectstore', $objectstore); + if ($wasGlobal && !$isGlobal) { + $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + } else if (!$wasGlobal && $isGlobal) { + $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); } - $allStorages[$id] = $updatedStorage; - $this->writeConfig($allStorages); + $changedConfig = array_diff_assoc($updatedStorage->getBackendOptions(), $oldStorage->getBackendOptions()); + $changedOptions = array_diff_assoc($updatedStorage->getMountOptions(), $oldStorage->getMountOptions()); + + foreach ($changedConfig as $key => $value) { + $this->dbConfig->setConfig($id, $key, $value); + } + foreach ($changedOptions as $key => $value) { + $this->dbConfig->setOption($id, $key, $value); + } $this->triggerChangeHooks($oldStorage, $updatedStorage); @@ -548,17 +402,15 @@ abstract class StoragesService { * @throws NotFoundException if no storage was found with the given id */ public function removeStorage($id) { - $allStorages = $this->readConfig(); + $existingMount = $this->dbConfig->getMountById($id); - if (!isset($allStorages[$id])) { + if (!is_array($existingMount)) { throw new NotFoundException('Storage with id "' . $id . '" not found'); } - $deletedStorage = $allStorages[$id]; - unset($allStorages[$id]); - - $this->writeConfig($allStorages); + $this->dbConfig->removeMount($id); + $deletedStorage = $this->getStorageConfigFromDBMount($existingMount); $this->triggerHooks($deletedStorage, Filesystem::signal_delete_mount); // delete oc_storages entries and oc_filecache @@ -578,24 +430,6 @@ abstract class StoragesService { } /** - * Generates a configuration id to use for a new configuration entry. - * - * @param array $allStorages array of all storage configs - * - * @return int id - */ - protected function generateNextId($allStorages) { - if (empty($allStorages)) { - return 1; - } - // note: this will mess up with with concurrency, - // but so did the mount.json. This horribly hack - // will disappear once we move to DB tables to - // store the config - return (max(array_keys($allStorages)) + 1); - } - - /** * Returns the rusty storage id from oc_storages from the given storage config. * * @param StorageConfig $storageConfig |