Browse Source
fix(sharing): add command to fix broken shares after ownership transferring
fix(sharing): add command to fix broken shares after ownership transferring
Signed-off-by: Luka Trovic <luka@nextcloud.com>pull/49612/head
6 changed files with 211 additions and 1 deletions
-
1apps/files_sharing/appinfo/info.xml
-
1apps/files_sharing/composer/composer/autoload_classmap.php
-
1apps/files_sharing/composer/composer/autoload_static.php
-
65apps/files_sharing/lib/Command/FixShareOwners.php
-
28apps/files_sharing/lib/OrphanHelper.php
-
116apps/files_sharing/tests/Command/FixShareOwnersTest.php
@ -0,0 +1,65 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
/** |
|||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors |
|||
* SPDX-License-Identifier: AGPL-3.0-or-later |
|||
*/ |
|||
|
|||
namespace OCA\Files_Sharing\Command; |
|||
|
|||
use OC\Core\Command\Base; |
|||
use OCA\Files_Sharing\OrphanHelper; |
|||
use Symfony\Component\Console\Input\InputInterface; |
|||
use Symfony\Component\Console\Input\InputOption; |
|||
use Symfony\Component\Console\Output\OutputInterface; |
|||
|
|||
class FixShareOwners extends Base { |
|||
public function __construct( |
|||
private readonly OrphanHelper $orphanHelper, |
|||
) { |
|||
parent::__construct(); |
|||
} |
|||
|
|||
protected function configure(): void { |
|||
$this |
|||
->setName('sharing:fix-share-owners') |
|||
->setDescription('Fix owner of broken shares after transfer ownership on old versions') |
|||
->addOption( |
|||
'dry-run', |
|||
null, |
|||
InputOption::VALUE_NONE, |
|||
'only show which shares would be updated' |
|||
); |
|||
} |
|||
|
|||
public function execute(InputInterface $input, OutputInterface $output): int { |
|||
$shares = $this->orphanHelper->getAllShares(); |
|||
$dryRun = $input->getOption('dry-run'); |
|||
$count = 0; |
|||
|
|||
foreach ($shares as $share) { |
|||
if ($this->orphanHelper->isShareValid($share['owner'], $share['fileid']) || !$this->orphanHelper->fileExists($share['fileid'])) { |
|||
continue; |
|||
} |
|||
|
|||
$owner = $this->orphanHelper->findOwner($share['fileid']); |
|||
|
|||
if ($owner !== null) { |
|||
if ($dryRun) { |
|||
$output->writeln("Share with id <info>{$share['id']}</info> (target: <info>{$share['target']}</info>) can be updated to owner <info>$owner</info>"); |
|||
} else { |
|||
$this->orphanHelper->updateShareOwner($share['id'], $owner); |
|||
$output->writeln("Share with id <info>{$share['id']}</info> (target: <info>{$share['target']}</info>) updated to owner <info>$owner</info>"); |
|||
} |
|||
$count++; |
|||
} |
|||
} |
|||
|
|||
if ($count === 0) { |
|||
$output->writeln('No broken shares detected'); |
|||
} |
|||
|
|||
return static::SUCCESS; |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
<?php |
|||
/** |
|||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors |
|||
* SPDX-License-Identifier: AGPL-3.0-only |
|||
*/ |
|||
namespace OCA\Files_Sharing\Tests\Command; |
|||
|
|||
use OCA\Files_Sharing\Command\FixShareOwners; |
|||
use OCA\Files_Sharing\OrphanHelper; |
|||
use Symfony\Component\Console\Input\InputInterface; |
|||
use Symfony\Component\Console\Output\OutputInterface; |
|||
use Test\TestCase; |
|||
|
|||
/** |
|||
* Class FixShareOwnersTest |
|||
* |
|||
* @package OCA\Files_Sharing\Tests\Command |
|||
*/ |
|||
class FixShareOwnersTest extends TestCase { |
|||
/** |
|||
* @var FixShareOwners |
|||
*/ |
|||
private $command; |
|||
|
|||
/** |
|||
* @var OrphanHelper|\PHPUnit\Framework\MockObject\MockObject |
|||
*/ |
|||
private $orphanHelper; |
|||
|
|||
protected function setUp(): void { |
|||
parent::setUp(); |
|||
|
|||
$this->orphanHelper = $this->createMock(OrphanHelper::class); |
|||
$this->command = new FixShareOwners($this->orphanHelper); |
|||
} |
|||
|
|||
public function testExecuteNoSharesDetected() { |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('getAllShares') |
|||
->willReturn([ |
|||
['id' => 1, 'owner' => 'user1', 'fileid' => 1, 'target' => 'target1'], |
|||
['id' => 2, 'owner' => 'user2', 'fileid' => 2, 'target' => 'target2'], |
|||
]); |
|||
$this->orphanHelper->expects($this->exactly(2)) |
|||
->method('isShareValid') |
|||
->willReturn(true); |
|||
|
|||
$input = $this->createMock(InputInterface::class); |
|||
$output = $this->createMock(OutputInterface::class); |
|||
|
|||
$output->expects($this->once()) |
|||
->method('writeln') |
|||
->with('No broken shares detected'); |
|||
$this->command->execute($input, $output); |
|||
} |
|||
|
|||
public function testExecuteSharesDetected() { |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('getAllShares') |
|||
->willReturn([ |
|||
['id' => 1, 'owner' => 'user1', 'fileid' => 1, 'target' => 'target1'], |
|||
['id' => 2, 'owner' => 'user2', 'fileid' => 2, 'target' => 'target2'], |
|||
]); |
|||
$this->orphanHelper->expects($this->exactly(2)) |
|||
->method('isShareValid') |
|||
->willReturnOnConsecutiveCalls(true, false); |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('fileExists') |
|||
->willReturn(true); |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('findOwner') |
|||
->willReturn('newOwner'); |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('updateShareOwner'); |
|||
|
|||
$input = $this->createMock(InputInterface::class); |
|||
$output = $this->createMock(OutputInterface::class); |
|||
|
|||
$output->expects($this->once()) |
|||
->method('writeln') |
|||
->with('Share with id <info>2</info> (target: <info>target2</info>) updated to owner <info>newOwner</info>'); |
|||
$this->command->execute($input, $output); |
|||
} |
|||
|
|||
public function testExecuteSharesDetectedDryRun() { |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('getAllShares') |
|||
->willReturn([ |
|||
['id' => 1, 'owner' => 'user1', 'fileid' => 1, 'target' => 'target1'], |
|||
['id' => 2, 'owner' => 'user2', 'fileid' => 2, 'target' => 'target2'], |
|||
]); |
|||
$this->orphanHelper->expects($this->exactly(2)) |
|||
->method('isShareValid') |
|||
->willReturnOnConsecutiveCalls(true, false); |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('fileExists') |
|||
->willReturn(true); |
|||
$this->orphanHelper->expects($this->once()) |
|||
->method('findOwner') |
|||
->willReturn('newOwner'); |
|||
$this->orphanHelper->expects($this->never()) |
|||
->method('updateShareOwner'); |
|||
|
|||
$input = $this->createMock(InputInterface::class); |
|||
$output = $this->createMock(OutputInterface::class); |
|||
|
|||
$output->expects($this->once()) |
|||
->method('writeln') |
|||
->with('Share with id <info>2</info> (target: <info>target2</info>) can be updated to owner <info>newOwner</info>'); |
|||
$input->expects($this->once()) |
|||
->method('getOption') |
|||
->with('dry-run') |
|||
->willReturn(true); |
|||
$this->command->execute($input, $output); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue