Merge pull request #44921 from nextcloud/backport/44904/stable28

backport/44039/stable28
Benjamin Gaussorgues 1 month ago committed by GitHub
commit 0a5d9827f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -12,6 +12,7 @@ declare(strict_types=1);
* @author Roeland Jago Douma <roeland@famdouma.nl> * @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com> * @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com>
* @author Tobia De Koninck <LEDfan@users.noreply.github.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 * @license GNU AGPL version 3 or any later version
* *
@ -33,6 +34,7 @@ declare(strict_types=1);
namespace OCA\Files\Service; namespace OCA\Files\Service;
use Closure; use Closure;
use OC\Encryption\Manager as EncryptionManager;
use OC\Files\Filesystem; use OC\Files\Filesystem;
use OC\Files\View; use OC\Files\View;
use OCA\Files\Exception\TransferOwnershipException; use OCA\Files\Exception\TransferOwnershipException;
@ -41,6 +43,7 @@ use OCP\Files\Config\IUserMountCache;
use OCP\Files\FileInfo; use OCP\Files\FileInfo;
use OCP\Files\IHomeStorage; use OCP\Files\IHomeStorage;
use OCP\Files\InvalidPathException; use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountManager;
use OCP\IUser; use OCP\IUser;
use OCP\IUserManager; use OCP\IUserManager;
@ -58,31 +61,16 @@ use function rtrim;
class OwnershipTransferService { class OwnershipTransferService {
/** @var IEncryptionManager */ private IEncryptionManager|EncryptionManager $encryptionManager;
private $encryptionManager;
/** @var IShareManager */ public function __construct(
private $shareManager; IEncryptionManager $encryptionManager,
private IShareManager $shareManager,
/** @var IMountManager */ private IMountManager $mountManager,
private $mountManager; private IUserMountCache $userMountCache,
private IUserManager $userManager,
/** @var IUserMountCache */ ) {
private $userMountCache; $this->encryptionManager = $encryptionManager;
/** @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;
} }
/** /**
@ -95,13 +83,15 @@ class OwnershipTransferService {
* @throws TransferOwnershipException * @throws TransferOwnershipException
* @throws \OC\User\NoUserException * @throws \OC\User\NoUserException
*/ */
public function transfer(IUser $sourceUser, public function transfer(
IUser $sourceUser,
IUser $destinationUser, IUser $destinationUser,
string $path, string $path,
?OutputInterface $output = null, ?OutputInterface $output = null,
bool $move = false, bool $move = false,
bool $firstLogin = false, bool $firstLogin = false,
bool $transferIncomingShares = false): void { bool $transferIncomingShares = false,
): void {
$output = $output ?? new NullOutput(); $output = $output ?? new NullOutput();
$sourceUid = $sourceUser->getUID(); $sourceUid = $sourceUser->getUID();
$destinationUid = $destinationUser->getUID(); $destinationUid = $destinationUser->getUID();
@ -183,10 +173,12 @@ class OwnershipTransferService {
$output $output
); );
$destinationPath = $finalTarget . '/' . $path;
// restore the shares // restore the shares
$this->restoreShares( $this->restoreShares(
$sourceUid, $sourceUid,
$destinationUid, $destinationUid,
$destinationPath,
$shares, $shares,
$output $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, OutputInterface $output,
View $view, View $view,
string $path): array { string $path,
): array {
$output->writeln("Collecting all share information for files and folders of $sourceUid ..."); $output->writeln("Collecting all share information for files and folders of $sourceUid ...");
$shares = []; $shares = [];
$progress = new ProgressBar($output); $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; $offset = 0;
while (true) { while (true) {
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset); $sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
@ -298,17 +309,17 @@ class OwnershipTransferService {
break; break;
} }
if ($path !== "$sourceUid/files") { 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 { try {
$relativePath = $view->getPath($share->getNodeId()); $relativePath = $view->getPath($share->getNodeId());
$singleFileTranfer = $view->is_file($path); $singleFileTranfer = $view->is_file($normalizedPath);
if ($singleFileTranfer) { if ($singleFileTranfer) {
return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path); return Filesystem::normalizePath($relativePath) === $normalizedPath;
} }
return mb_strpos( return mb_strpos(
Filesystem::normalizePath($relativePath . '/', false), Filesystem::normalizePath($relativePath . '/', false),
Filesystem::normalizePath($path . '/', false)) === 0; $normalizedPath . '/') === 0;
} catch (\Exception $e) { } catch (\Exception $e) {
return false; return false;
} }
@ -321,7 +332,11 @@ class OwnershipTransferService {
$progress->finish(); $progress->finish();
$output->writeln(''); $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, 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 $destinationUid,
string $targetLocation,
array $shares, array $shares,
OutputInterface $output) { OutputInterface $output,
):void {
$output->writeln("Restoring shares ..."); $output->writeln("Restoring shares ...");
$progress = new ProgressBar($output, count($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 { try {
if ($share->getShareType() === IShare::TYPE_USER && if ($share->getShareType() === IShare::TYPE_USER &&
$share->getSharedWith() === $destinationUid) { $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 // 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 // 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(); $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); $this->shareManager->updateShare($share);
} }

Loading…
Cancel
Save