LegacyStoragesService.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\Files_External\Service;
  8. use OCA\Files_External\Lib\StorageConfig;
  9. use Psr\Log\LoggerInterface;
  10. /**
  11. * Read mount config from legacy mount.json
  12. */
  13. abstract class LegacyStoragesService {
  14. /** @var BackendService */
  15. protected $backendService;
  16. /**
  17. * Read legacy config data
  18. *
  19. * @return array list of mount configs
  20. */
  21. abstract protected function readLegacyConfig();
  22. /**
  23. * Copy legacy storage options into the given storage config object.
  24. *
  25. * @param StorageConfig $storageConfig storage config to populate
  26. * @param string $mountType mount type
  27. * @param string $applicable applicable user or group
  28. * @param array $storageOptions legacy storage options
  29. *
  30. * @return StorageConfig populated storage config
  31. */
  32. protected function populateStorageConfigWithLegacyOptions(
  33. &$storageConfig,
  34. $mountType,
  35. $applicable,
  36. $storageOptions
  37. ) {
  38. $backend = $this->backendService->getBackend($storageOptions['backend']);
  39. if (!$backend) {
  40. throw new \UnexpectedValueException('Invalid backend ' . $storageOptions['backend']);
  41. }
  42. $storageConfig->setBackend($backend);
  43. if (isset($storageOptions['authMechanism']) && $storageOptions['authMechanism'] !== 'builtin::builtin') {
  44. $authMechanism = $this->backendService->getAuthMechanism($storageOptions['authMechanism']);
  45. } else {
  46. $authMechanism = $backend->getLegacyAuthMechanism($storageOptions);
  47. $storageOptions['authMechanism'] = 'null'; // to make error handling easier
  48. }
  49. if (!$authMechanism) {
  50. throw new \UnexpectedValueException('Invalid authentication mechanism ' . $storageOptions['authMechanism']);
  51. }
  52. $storageConfig->setAuthMechanism($authMechanism);
  53. $storageConfig->setBackendOptions($storageOptions['options']);
  54. if (isset($storageOptions['mountOptions'])) {
  55. $storageConfig->setMountOptions($storageOptions['mountOptions']);
  56. }
  57. if (!isset($storageOptions['priority'])) {
  58. $storageOptions['priority'] = $backend->getPriority();
  59. }
  60. $storageConfig->setPriority($storageOptions['priority']);
  61. if ($mountType === \OCA\Files_External\MountConfig::MOUNT_TYPE_USER) {
  62. $applicableUsers = $storageConfig->getApplicableUsers();
  63. if ($applicable !== 'all') {
  64. $applicableUsers[] = $applicable;
  65. $storageConfig->setApplicableUsers($applicableUsers);
  66. }
  67. } elseif ($mountType === \OCA\Files_External\MountConfig::MOUNT_TYPE_GROUP) {
  68. $applicableGroups = $storageConfig->getApplicableGroups();
  69. $applicableGroups[] = $applicable;
  70. $storageConfig->setApplicableGroups($applicableGroups);
  71. }
  72. return $storageConfig;
  73. }
  74. /**
  75. * Read the external storage config
  76. *
  77. * @return StorageConfig[] map of storage id to storage config
  78. */
  79. public function getAllStorages() {
  80. $mountPoints = $this->readLegacyConfig();
  81. /**
  82. * Here is the how the horribly messy mount point array looks like
  83. * from the mount.json file:
  84. *
  85. * $storageOptions = $mountPoints[$mountType][$applicable][$mountPath]
  86. *
  87. * - $mountType is either "user" or "group"
  88. * - $applicable is the name of a user or group (or the current user for personal mounts)
  89. * - $mountPath is the mount point path (where the storage must be mounted)
  90. * - $storageOptions is a map of storage options:
  91. * - "priority": storage priority
  92. * - "backend": backend identifier
  93. * - "class": LEGACY backend class name
  94. * - "options": backend-specific options
  95. * - "authMechanism": authentication mechanism identifier
  96. * - "mountOptions": mount-specific options (ex: disable previews, scanner, etc)
  97. */
  98. // group by storage id
  99. /** @var StorageConfig[] $storages */
  100. $storages = [];
  101. // for storages without id (legacy), group by config hash for
  102. // later processing
  103. $storagesWithConfigHash = [];
  104. foreach ($mountPoints as $mountType => $applicables) {
  105. foreach ($applicables as $applicable => $mountPaths) {
  106. foreach ($mountPaths as $rootMountPath => $storageOptions) {
  107. $currentStorage = null;
  108. /**
  109. * Flag whether the config that was read already has an id.
  110. * If not, it will use a config hash instead and generate
  111. * a proper id later
  112. *
  113. * @var boolean
  114. */
  115. $hasId = false;
  116. // the root mount point is in the format "/$user/files/the/mount/point"
  117. // we remove the "/$user/files" prefix
  118. $parts = explode('/', ltrim($rootMountPath, '/'), 3);
  119. if (count($parts) < 3) {
  120. // something went wrong, skip
  121. \OC::$server->get(LoggerInterface::class)->error('Could not parse mount point "' . $rootMountPath . '"', ['app' => 'files_external']);
  122. continue;
  123. }
  124. $relativeMountPath = rtrim($parts[2], '/');
  125. // note: we cannot do this after the loop because the decrypted config
  126. // options might be needed for the config hash
  127. $storageOptions['options'] = \OCA\Files_External\MountConfig::decryptPasswords($storageOptions['options']);
  128. if (!isset($storageOptions['backend'])) {
  129. $storageOptions['backend'] = $storageOptions['class']; // legacy compat
  130. }
  131. if (!isset($storageOptions['authMechanism'])) {
  132. $storageOptions['authMechanism'] = null; // ensure config hash works
  133. }
  134. if (isset($storageOptions['id'])) {
  135. $configId = (int)$storageOptions['id'];
  136. if (isset($storages[$configId])) {
  137. $currentStorage = $storages[$configId];
  138. }
  139. $hasId = true;
  140. } else {
  141. // missing id in legacy config, need to generate
  142. // but at this point we don't know the max-id, so use
  143. // first group it by config hash
  144. $storageOptions['mountpoint'] = $rootMountPath;
  145. $configId = \OCA\Files_External\MountConfig::makeConfigHash($storageOptions);
  146. if (isset($storagesWithConfigHash[$configId])) {
  147. $currentStorage = $storagesWithConfigHash[$configId];
  148. }
  149. }
  150. if (is_null($currentStorage)) {
  151. // create new
  152. $currentStorage = new StorageConfig($configId);
  153. $currentStorage->setMountPoint($relativeMountPath);
  154. }
  155. try {
  156. $this->populateStorageConfigWithLegacyOptions(
  157. $currentStorage,
  158. $mountType,
  159. $applicable,
  160. $storageOptions
  161. );
  162. if ($hasId) {
  163. $storages[$configId] = $currentStorage;
  164. } else {
  165. $storagesWithConfigHash[$configId] = $currentStorage;
  166. }
  167. } catch (\UnexpectedValueException $e) {
  168. // don't die if a storage backend doesn't exist
  169. \OC::$server->get(LoggerInterface::class)->error('Could not load storage.', [
  170. 'app' => 'files_external',
  171. 'exception' => $e,
  172. ]);
  173. }
  174. }
  175. }
  176. }
  177. // convert parameter values
  178. foreach ($storages as $storage) {
  179. $storage->getBackend()->validateStorageDefinition($storage);
  180. $storage->getAuthMechanism()->validateStorageDefinition($storage);
  181. }
  182. return $storages;
  183. }
  184. }