Update.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Encryption;
  8. use InvalidArgumentException;
  9. use OC\Files\Filesystem;
  10. use OC\Files\Mount;
  11. use OC\Files\View;
  12. use OCP\Encryption\Exceptions\GenericEncryptionException;
  13. use Psr\Log\LoggerInterface;
  14. /**
  15. * update encrypted files, e.g. because a file was shared
  16. */
  17. class Update {
  18. /** @var View */
  19. protected $view;
  20. /** @var Util */
  21. protected $util;
  22. /** @var \OC\Files\Mount\Manager */
  23. protected $mountManager;
  24. /** @var Manager */
  25. protected $encryptionManager;
  26. /** @var string */
  27. protected $uid;
  28. /** @var File */
  29. protected $file;
  30. /** @var LoggerInterface */
  31. protected $logger;
  32. /**
  33. * @param string $uid
  34. */
  35. public function __construct(
  36. View $view,
  37. Util $util,
  38. Mount\Manager $mountManager,
  39. Manager $encryptionManager,
  40. File $file,
  41. LoggerInterface $logger,
  42. $uid
  43. ) {
  44. $this->view = $view;
  45. $this->util = $util;
  46. $this->mountManager = $mountManager;
  47. $this->encryptionManager = $encryptionManager;
  48. $this->file = $file;
  49. $this->logger = $logger;
  50. $this->uid = $uid;
  51. }
  52. /**
  53. * hook after file was shared
  54. *
  55. * @param array $params
  56. */
  57. public function postShared($params) {
  58. if ($this->encryptionManager->isEnabled()) {
  59. if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
  60. $path = Filesystem::getPath($params['fileSource']);
  61. [$owner, $ownerPath] = $this->getOwnerPath($path);
  62. $absPath = '/' . $owner . '/files/' . $ownerPath;
  63. $this->update($absPath);
  64. }
  65. }
  66. }
  67. /**
  68. * hook after file was unshared
  69. *
  70. * @param array $params
  71. */
  72. public function postUnshared($params) {
  73. if ($this->encryptionManager->isEnabled()) {
  74. if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
  75. $path = Filesystem::getPath($params['fileSource']);
  76. [$owner, $ownerPath] = $this->getOwnerPath($path);
  77. $absPath = '/' . $owner . '/files/' . $ownerPath;
  78. $this->update($absPath);
  79. }
  80. }
  81. }
  82. /**
  83. * inform encryption module that a file was restored from the trash bin,
  84. * e.g. to update the encryption keys
  85. *
  86. * @param array $params
  87. */
  88. public function postRestore($params) {
  89. if ($this->encryptionManager->isEnabled()) {
  90. $path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $params['filePath']);
  91. $this->update($path);
  92. }
  93. }
  94. /**
  95. * inform encryption module that a file was renamed,
  96. * e.g. to update the encryption keys
  97. *
  98. * @param array $params
  99. */
  100. public function postRename($params) {
  101. $source = $params['oldpath'];
  102. $target = $params['newpath'];
  103. if (
  104. $this->encryptionManager->isEnabled() &&
  105. dirname($source) !== dirname($target)
  106. ) {
  107. [$owner, $ownerPath] = $this->getOwnerPath($target);
  108. $absPath = '/' . $owner . '/files/' . $ownerPath;
  109. $this->update($absPath);
  110. }
  111. }
  112. /**
  113. * get owner and path relative to data/<owner>/files
  114. *
  115. * @param string $path path to file for current user
  116. * @return array ['owner' => $owner, 'path' => $path]
  117. * @throw \InvalidArgumentException
  118. */
  119. protected function getOwnerPath($path) {
  120. $info = Filesystem::getFileInfo($path);
  121. $owner = Filesystem::getOwner($path);
  122. $view = new View('/' . $owner . '/files');
  123. $path = $view->getPath($info->getId());
  124. if ($path === null) {
  125. throw new InvalidArgumentException('No file found for ' . $info->getId());
  126. }
  127. return [$owner, $path];
  128. }
  129. /**
  130. * notify encryption module about added/removed users from a file/folder
  131. *
  132. * @param string $path relative to data/
  133. * @throws Exceptions\ModuleDoesNotExistsException
  134. */
  135. public function update($path) {
  136. $encryptionModule = $this->encryptionManager->getEncryptionModule();
  137. // if the encryption module doesn't encrypt the files on a per-user basis
  138. // we have nothing to do here.
  139. if ($encryptionModule->needDetailedAccessList() === false) {
  140. return;
  141. }
  142. // if a folder was shared, get a list of all (sub-)folders
  143. if ($this->view->is_dir($path)) {
  144. $allFiles = $this->util->getAllFiles($path);
  145. } else {
  146. $allFiles = [$path];
  147. }
  148. foreach ($allFiles as $file) {
  149. $usersSharing = $this->file->getAccessList($file);
  150. try {
  151. $encryptionModule->update($file, $this->uid, $usersSharing);
  152. } catch (GenericEncryptionException $e) {
  153. // If the update of an individual file fails e.g. due to a corrupt key we should continue the operation and just log the failure
  154. $this->logger->error('Failed to update encryption module for ' . $this->uid . ' ' . $file, [ 'exception' => $e ]);
  155. }
  156. }
  157. }
  158. }