Merge pull request #54263 from nextcloud/backport/54233/stable30

This commit is contained in:
Benjamin Gaussorgues 2025-08-07 16:03:53 +02:00 committed by GitHub
commit f6504e7f02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 102 additions and 141 deletions

View file

@ -12,6 +12,7 @@ use OC\Files\View;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Users\Setup;
use OCA\Encryption\Util;
use OCP\Files\FileInfo;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IUser;
@ -246,15 +247,19 @@ class EncryptAll {
while ($root = array_pop($directories)) {
$content = $this->rootView->getDirectoryContent($root);
foreach ($content as $file) {
$path = $root . '/' . $file['name'];
if ($this->rootView->is_dir($path)) {
$path = $root . '/' . $file->getName();
if ($file->isShared()) {
$progress->setMessage("Skip shared file/folder $path");
$progress->advance();
continue;
} elseif ($file->getType() === FileInfo::TYPE_FOLDER) {
$directories[] = $path;
continue;
} else {
$progress->setMessage("encrypt files for user $userCount: $path");
$progress->advance();
try {
if ($this->encryptFile($path) === false) {
if ($this->encryptFile($file, $path) === false) {
$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
$progress->advance();
}
@ -275,17 +280,9 @@ class EncryptAll {
}
}
/**
* encrypt file
*
* @param string $path
* @return bool
*/
protected function encryptFile($path) {
protected function encryptFile(FileInfo $fileInfo, string $path): bool {
// skip already encrypted files
$fileInfo = $this->rootView->getFileInfo($path);
if ($fileInfo !== false && $fileInfo->isEncrypted()) {
if ($fileInfo->isEncrypted()) {
return true;
}

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@ -31,52 +33,23 @@ use Test\TestCase;
class EncryptAllTest extends TestCase {
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCA\Encryption\KeyManager */
protected $keyManager;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCA\Encryption\Util */
protected $util;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\IUserManager */
protected $userManager;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCA\Encryption\Users\Setup */
protected $setupUser;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OC\Files\View */
protected $view;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\IConfig */
protected $config;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\Mail\IMailer */
protected $mailer;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\IL10N */
protected $l;
/** @var \PHPUnit\Framework\MockObject\MockObject | IFactory */
protected $l10nFactory;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Helper\QuestionHelper */
protected $questionHelper;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Input\InputInterface */
protected $inputInterface;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Output\OutputInterface */
protected $outputInterface;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\UserInterface */
protected $userInterface;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\Security\ISecureRandom */
protected $secureRandom;
protected KeyManager&MockObject $keyManager;
protected Util&MockObject $util;
protected IUserManager&MockObject $userManager;
protected Setup&MockObject $setupUser;
protected View&MockObject $view;
protected IConfig&MockObject $config;
protected IMailer&MockObject $mailer;
protected IL10N&MockObject $l;
protected IFactory&MockObject $l10nFactory;
protected \Symfony\Component\Console\Helper\QuestionHelper&MockObject $questionHelper;
protected \Symfony\Component\Console\Input\InputInterface&MockObject $inputInterface;
protected \Symfony\Component\Console\Output\OutputInterface&MockObject $outputInterface;
protected UserInterface&MockObject $userInterface;
protected ISecureRandom&MockObject $secureRandom;
protected LoggerInterface&MockObject $logger;
/** @var EncryptAll */
protected $encryptAll;
protected EncryptAll $encryptAll;
protected function setUp(): void {
parent::setUp();
@ -109,7 +82,7 @@ class EncryptAllTest extends TestCase {
/**
* We need format method to return a string
* @var OutputFormatterInterface|\PHPUnit\Framework\MockObject\MockObject
* @var OutputFormatterInterface&MockObject
*/
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
$outputFormatter->method('isDecorated')->willReturn(false);
@ -141,6 +114,13 @@ class EncryptAllTest extends TestCase {
);
}
protected function createFileInfoMock($type, string $name): FileInfo&MockObject {
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('getType')->willReturn($type);
$fileInfo->method('getName')->willReturn($name);
return $fileInfo;
}
public function testEncryptAll(): void {
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
@ -160,7 +140,7 @@ class EncryptAllTest extends TestCase {
$this->logger,
]
)
->setMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
->getMock();
$this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
@ -190,7 +170,7 @@ class EncryptAllTest extends TestCase {
$this->logger,
]
)
->setMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
->getMock();
$this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(true);
@ -221,7 +201,7 @@ class EncryptAllTest extends TestCase {
$this->logger,
]
)
->setMethods(['setupUserFS', 'generateOneTimePassword'])
->onlyMethods(['setupUserFS', 'generateOneTimePassword'])
->getMock();
@ -253,8 +233,8 @@ class EncryptAllTest extends TestCase {
$this->assertSame('', $userPasswords['user2']);
}
public function testEncryptAllUsersFiles() {
/** @var EncryptAll | \PHPUnit\Framework\MockObject\MockObject $encryptAll */
public function testEncryptAllUsersFiles(): void {
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
->setConstructorArgs(
[
@ -272,7 +252,7 @@ class EncryptAllTest extends TestCase {
$this->logger,
]
)
->setMethods(['encryptUsersFiles'])
->onlyMethods(['encryptUsersFiles'])
->getMock();
$this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
@ -281,17 +261,22 @@ class EncryptAllTest extends TestCase {
$this->invokePrivate($encryptAll, 'output', [$this->outputInterface]);
$this->invokePrivate($encryptAll, 'userPasswords', [['user1' => 'pwd1', 'user2' => 'pwd2']]);
$encryptAll->expects($this->exactly(2))->method('encryptUsersFiles')
->withConsecutive(
['user1'],
['user2'],
);
$encryptAllCalls = [];
$encryptAll->expects($this->exactly(2))
->method('encryptUsersFiles')
->willReturnCallback(function ($uid) use (&$encryptAllCalls): void {
$encryptAllCalls[] = $uid;
});
$this->invokePrivate($encryptAll, 'encryptAllUsersFiles');
self::assertEquals([
'user1',
'user2',
], $encryptAllCalls);
}
public function testEncryptUsersFiles() {
/** @var EncryptAll | \PHPUnit\Framework\MockObject\MockObject $encryptAll */
public function testEncryptUsersFiles(): void {
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
->setConstructorArgs(
[
@ -309,40 +294,39 @@ class EncryptAllTest extends TestCase {
$this->logger,
]
)
->setMethods(['encryptFile', 'setupUserFS'])
->onlyMethods(['encryptFile', 'setupUserFS'])
->getMock();
$this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
$this->view->expects($this->exactly(2))->method('getDirectoryContent')
->withConsecutive(
['/user1/files'],
['/user1/files/foo'],
)->willReturnOnConsecutiveCalls(
->willReturnMap([
[
['name' => 'foo', 'type' => 'dir'],
['name' => 'bar', 'type' => 'file'],
'/user1/files',
'',
null,
[
$this->createFileInfoMock(FileInfo::TYPE_FOLDER, 'foo'),
$this->createFileInfoMock(FileInfo::TYPE_FILE, 'bar'),
],
],
[
['name' => 'subfile', 'type' => 'file']
]
);
'/user1/files/foo',
'',
null,
[
$this->createFileInfoMock(FileInfo::TYPE_FILE, 'subfile'),
],
],
]);
$this->view->expects($this->any())->method('is_dir')
->willReturnCallback(
function ($path) {
if ($path === '/user1/files/foo') {
return true;
}
return false;
}
);
$encryptAll->expects($this->exactly(2))->method('encryptFile')
->withConsecutive(
['/user1/files/bar'],
['/user1/files/foo/subfile'],
);
$encryptAllCalls = [];
$encryptAll->expects($this->exactly(2))
->method('encryptFile')
->willReturnCallback(function (FileInfo $file, string $path) use (&$encryptAllCalls): bool {
$encryptAllCalls[] = $path;
return true;
});
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
$outputFormatter->method('isDecorated')->willReturn(false);
@ -352,9 +336,13 @@ class EncryptAllTest extends TestCase {
$progressBar = new ProgressBar($this->outputInterface);
$this->invokePrivate($encryptAll, 'encryptUsersFiles', ['user1', $progressBar, '']);
self::assertEquals([
'/user1/files/bar',
'/user1/files/foo/subfile',
], $encryptAllCalls);
}
public function testGenerateOneTimePassword() {
public function testGenerateOneTimePassword(): void {
$password = $this->invokePrivate($this->encryptAll, 'generateOneTimePassword', ['user1']);
$this->assertTrue(is_string($password));
$this->assertSame(8, strlen($password));
@ -368,12 +356,11 @@ class EncryptAllTest extends TestCase {
* @dataProvider dataTestEncryptFile
* @param $isEncrypted
*/
public function testEncryptFile($isEncrypted) {
public function testEncryptFile($isEncrypted): void {
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->expects($this->any())->method('isEncrypted')
->willReturn($isEncrypted);
$this->view->expects($this->any())->method('getFileInfo')
->willReturn($fileInfo);
$this->view->expects($this->never())->method('getFileInfo');
if ($isEncrypted) {
@ -385,11 +372,11 @@ class EncryptAllTest extends TestCase {
}
$this->assertTrue(
$this->invokePrivate($this->encryptAll, 'encryptFile', ['foo.txt'])
$this->invokePrivate($this->encryptAll, 'encryptFile', [$fileInfo, 'foo.txt'])
);
}
public function dataTestEncryptFile() {
public static function dataTestEncryptFile(): array {
return [
[true],
[false],

View file

@ -13,59 +13,36 @@ use OCP\App\IAppManager;
use OCP\Encryption\IEncryptionModule;
use OCP\Encryption\IManager;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Test\TestCase;
class EncryptAllTest extends TestCase {
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\IConfig */
protected $config;
private IConfig&MockObject $config;
private IManager&MockObject $encryptionManager;
private IAppManager&MockObject $appManager;
private InputInterface&MockObject $consoleInput;
private OutputInterface&MockObject $consoleOutput;
private QuestionHelper&MockObject $questionHelper;
private IEncryptionModule&MockObject $encryptionModule;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\Encryption\IManager */
protected $encryptionManager;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\App\IAppManager */
protected $appManager;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Input\InputInterface */
protected $consoleInput;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Output\OutputInterface */
protected $consoleOutput;
/** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Helper\QuestionHelper */
protected $questionHelper;
/** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\Encryption\IEncryptionModule */
protected $encryptionModule;
/** @var EncryptAll */
protected $command;
private EncryptAll $command;
protected function setUp(): void {
parent::setUp();
$this->config = $this->getMockBuilder(IConfig::class)
->disableOriginalConstructor()
->getMock();
$this->encryptionManager = $this->getMockBuilder(IManager::class)
->disableOriginalConstructor()
->getMock();
$this->appManager = $this->getMockBuilder(IAppManager::class)
->disableOriginalConstructor()
->getMock();
$this->encryptionModule = $this->getMockBuilder(IEncryptionModule::class)
->disableOriginalConstructor()
->getMock();
$this->questionHelper = $this->getMockBuilder(QuestionHelper::class)
->disableOriginalConstructor()
->getMock();
$this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
$this->config = $this->createMock(IConfig::class);
$this->encryptionManager = $this->createMock(IManager::class);
$this->appManager = $this->createMock(IAppManager::class);
$this->encryptionModule = $this->createMock(IEncryptionModule::class);
$this->questionHelper = $this->createMock(QuestionHelper::class);
$this->consoleInput = $this->createMock(InputInterface::class);
$this->consoleInput->expects($this->any())
->method('isInteractive')
->willReturn(true);
$this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
$this->consoleOutput = $this->createMock(OutputInterface::class);
}
public function testEncryptAll() {