LegacyStoragesService.php 6.8 KB

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