aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>2024-04-18 21:00:49 +0200
committerGitHub <noreply@github.com>2024-04-18 21:00:49 +0200
commit146675b675ae94fc856a526148557180249ca9c5 (patch)
tree3aa85b300ff75dd12ed64df559762d79f9b90082
parent0da51f5d28b2b31bc977acd5a5605a1eef3af953 (diff)
parent9cdbff8ec85e80b6aa6a4653ef510f1b17709785 (diff)
downloadnextcloud-server-146675b675ae94fc856a526148557180249ca9c5.tar.gz
nextcloud-server-146675b675ae94fc856a526148557180249ca9c5.zip
Merge pull request #44922 from nextcloud/backport/44904/stable29
-rw-r--r--apps/files/lib/Service/OwnershipTransferService.php111
1 files changed, 73 insertions, 38 deletions
diff --git a/apps/files/lib/Service/OwnershipTransferService.php b/apps/files/lib/Service/OwnershipTransferService.php
index 77a4f8c0997..1a28785fa1f 100644
--- a/apps/files/lib/Service/OwnershipTransferService.php
+++ b/apps/files/lib/Service/OwnershipTransferService.php
@@ -12,6 +12,7 @@ declare(strict_types=1);
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com>
* @author Tobia De Koninck <LEDfan@users.noreply.github.com>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
*
* @license GNU AGPL version 3 or any later version
*
@@ -33,6 +34,7 @@ declare(strict_types=1);
namespace OCA\Files\Service;
use Closure;
+use OC\Encryption\Manager as EncryptionManager;
use OC\Files\Filesystem;
use OC\Files\View;
use OCA\Files\Exception\TransferOwnershipException;
@@ -41,6 +43,7 @@ use OCP\Files\Config\IUserMountCache;
use OCP\Files\FileInfo;
use OCP\Files\IHomeStorage;
use OCP\Files\InvalidPathException;
+use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\IUser;
use OCP\IUserManager;
@@ -58,31 +61,16 @@ use function rtrim;
class OwnershipTransferService {
- /** @var IEncryptionManager */
- private $encryptionManager;
+ private IEncryptionManager|EncryptionManager $encryptionManager;
- /** @var IShareManager */
- private $shareManager;
-
- /** @var IMountManager */
- private $mountManager;
-
- /** @var IUserMountCache */
- private $userMountCache;
-
- /** @var IUserManager */
- private $userManager;
-
- public function __construct(IEncryptionManager $manager,
- IShareManager $shareManager,
- IMountManager $mountManager,
- IUserMountCache $userMountCache,
- IUserManager $userManager) {
- $this->encryptionManager = $manager;
- $this->shareManager = $shareManager;
- $this->mountManager = $mountManager;
- $this->userMountCache = $userMountCache;
- $this->userManager = $userManager;
+ public function __construct(
+ IEncryptionManager $encryptionManager,
+ private IShareManager $shareManager,
+ private IMountManager $mountManager,
+ private IUserMountCache $userMountCache,
+ private IUserManager $userManager,
+ ) {
+ $this->encryptionManager = $encryptionManager;
}
/**
@@ -95,13 +83,15 @@ class OwnershipTransferService {
* @throws TransferOwnershipException
* @throws \OC\User\NoUserException
*/
- public function transfer(IUser $sourceUser,
+ public function transfer(
+ IUser $sourceUser,
IUser $destinationUser,
string $path,
?OutputInterface $output = null,
bool $move = false,
bool $firstLogin = false,
- bool $transferIncomingShares = false): void {
+ bool $transferIncomingShares = false,
+ ): void {
$output = $output ?? new NullOutput();
$sourceUid = $sourceUser->getUID();
$destinationUid = $destinationUser->getUID();
@@ -183,10 +173,12 @@ class OwnershipTransferService {
$output
);
+ $destinationPath = $finalTarget . '/' . $path;
// restore the shares
$this->restoreShares(
$sourceUid,
$destinationUid,
+ $destinationPath,
$shares,
$output
);
@@ -280,16 +272,35 @@ class OwnershipTransferService {
}
}
- private function collectUsersShares(string $sourceUid,
+ /**
+ * @return array<array{share: IShare, suffix: string}>
+ */
+ private function collectUsersShares(
+ string $sourceUid,
OutputInterface $output,
View $view,
- string $path): array {
+ string $path,
+ ): array {
$output->writeln("Collecting all share information for files and folders of $sourceUid ...");
$shares = [];
$progress = new ProgressBar($output);
- foreach ([IShare::TYPE_GROUP, IShare::TYPE_USER, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_ROOM, IShare::TYPE_EMAIL, IShare::TYPE_CIRCLE, IShare::TYPE_DECK, IShare::TYPE_SCIENCEMESH] as $shareType) {
+ $normalizedPath = Filesystem::normalizePath($path);
+
+ $supportedShareTypes = [
+ IShare::TYPE_GROUP,
+ IShare::TYPE_USER,
+ IShare::TYPE_LINK,
+ IShare::TYPE_REMOTE,
+ IShare::TYPE_ROOM,
+ IShare::TYPE_EMAIL,
+ IShare::TYPE_CIRCLE,
+ IShare::TYPE_DECK,
+ IShare::TYPE_SCIENCEMESH,
+ ];
+
+ foreach ($supportedShareTypes as $shareType) {
$offset = 0;
while (true) {
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
@@ -298,17 +309,17 @@ class OwnershipTransferService {
break;
}
if ($path !== "$sourceUid/files") {
- $sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
+ $sharePage = array_filter($sharePage, function (IShare $share) use ($view, $normalizedPath) {
try {
$relativePath = $view->getPath($share->getNodeId());
- $singleFileTranfer = $view->is_file($path);
+ $singleFileTranfer = $view->is_file($normalizedPath);
if ($singleFileTranfer) {
- return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path);
+ return Filesystem::normalizePath($relativePath) === $normalizedPath;
}
return mb_strpos(
Filesystem::normalizePath($relativePath . '/', false),
- Filesystem::normalizePath($path . '/', false)) === 0;
+ $normalizedPath . '/') === 0;
} catch (\Exception $e) {
return false;
}
@@ -321,7 +332,11 @@ class OwnershipTransferService {
$progress->finish();
$output->writeln('');
- return $shares;
+
+ return array_map(fn (IShare $share) => [
+ 'share' => $share,
+ 'suffix' => substr(Filesystem::normalizePath($view->getPath($share->getNodeId())), strlen($normalizedPath)),
+ ], $shares);
}
private function collectIncomingShares(string $sourceUid,
@@ -384,14 +399,22 @@ class OwnershipTransferService {
}
}
- private function restoreShares(string $sourceUid,
+ /**
+ * @param string $targetLocation New location of the transfered node
+ * @param array<array{share: IShare, suffix: string}> $shares previously collected share information
+ */
+ private function restoreShares(
+ string $sourceUid,
string $destinationUid,
+ string $targetLocation,
array $shares,
- OutputInterface $output) {
+ OutputInterface $output,
+ ):void {
$output->writeln("Restoring shares ...");
$progress = new ProgressBar($output, count($shares));
+ $rootFolder = \OCP\Server::get(IRootFolder::class);
- foreach ($shares as $share) {
+ foreach ($shares as ['share' => $share, 'suffix' => $suffix]) {
try {
if ($share->getShareType() === IShare::TYPE_USER &&
$share->getSharedWith() === $destinationUid) {
@@ -419,7 +442,19 @@ class OwnershipTransferService {
// trigger refetching of the node so that the new owner and mountpoint are taken into account
// otherwise the checks on the share update will fail due to the original node not being available in the new user scope
$this->userMountCache->clear();
- $share->setNodeId($share->getNode()->getId());
+
+ try {
+ // Try to get the "old" id.
+ // Normally the ID is preserved,
+ // but for transferes between different storages the ID might change
+ $newNodeId = $share->getNode()->getId();
+ } catch (\OCP\Files\NotFoundException) {
+ // ID has changed due to transfer between different storages
+ // Try to get the new ID from the target path and suffix of the share
+ $node = $rootFolder->get(Filesystem::normalizePath($targetLocation . '/' . $suffix));
+ $newNodeId = $node->getId();
+ }
+ $share->setNodeId($newNodeId);
$this->shareManager->updateShare($share);
}