diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index 907a6437f5b3..6eff66e72bea 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -547,4 +547,17 @@ protected function stripPartFileExtension($path) { return $path; } + /** + * Check if the module is ready to be used by that specific user. + * In case a module is not ready - because e.g. key pairs have not been generated + * upon login this method can return false before any operation starts and might + * cause issues during operations. + * + * @param string $user + * @return boolean + * @since 9.1.0 + */ + public function isReadyForUser($user) { + return $this->keyManager->userHasKeys($user); + } } diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index 12fa5f92bd5e..0accfb7900ac 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -493,6 +493,7 @@ public function getShareKey($path, $uid) { */ public function userHasKeys($userId) { $privateKey = $publicKey = true; + $exception = null; try { $this->getPrivateKey($userId); diff --git a/apps/files/command/transferownership.php b/apps/files/command/transferownership.php index 6bf2fae6bdfc..1f46efdde0d8 100644 --- a/apps/files/command/transferownership.php +++ b/apps/files/command/transferownership.php @@ -97,6 +97,12 @@ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln("Unknown destination user $this->destinationUser"); return; } + + // target user has to be ready + if (!\OC::$server->getEncryptionManager()->isReadyForUser($this->destinationUser)) { + $output->writeln("The target user is not ready to accept files. The user has at least to be logged in once."); + return; + } $date = date('c'); $this->finalTarget = "$this->destinationUser/files/transferred from $this->sourceUser on $date"; diff --git a/lib/private/encryption/manager.php b/lib/private/encryption/manager.php index d45bbf07ee96..8714d1618074 100644 --- a/lib/private/encryption/manager.php +++ b/lib/private/encryption/manager.php @@ -117,6 +117,25 @@ public function isReady() { } /** + * @param string $user + */ + public function isReadyForUser($user) { + if (!$this->isReady()) { + return false; + } + + foreach ($this->getEncryptionModules() as $module) { + /** @var IEncryptionModule $m */ + $m = call_user_func($module['callback']); + if (!$m->isReadyForUser($user)) { + return false; + } + } + + return true; + } + + /** * Registers an callback function which must return an encryption module instance * * @param string $id diff --git a/lib/public/encryption/iencryptionmodule.php b/lib/public/encryption/iencryptionmodule.php index df30dd57cee7..8d20a1ab57d1 100644 --- a/lib/public/encryption/iencryptionmodule.php +++ b/lib/public/encryption/iencryptionmodule.php @@ -168,4 +168,16 @@ public function encryptAll(InputInterface $input, OutputInterface $output); */ public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = ''); + /** + * Check if the module is ready to be used by that specific user. + * In case a module is not ready - because e.g. key pairs have not been generated + * upon login this method can return false before any operation starts and might + * cause issues during operations. + * + * @param string $user + * @return boolean + * @since 9.1.0 + */ + public function isReadyForUser($user); + } diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php index 651299a3eaba..21f7d9f09b92 100644 --- a/tests/lib/files/storage/wrapper/encryption.php +++ b/tests/lib/files/storage/wrapper/encryption.php @@ -5,6 +5,7 @@ use OC\Encryption\Util; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\User\Manager; use Test\Files\Storage\Storage; class Encryption extends Storage { @@ -118,7 +119,7 @@ protected function setUp() { $this->util = $this->getMock( '\OC\Encryption\Util', ['getUidAndFilename', 'isFile', 'isExcluded'], - [new View(), new \OC\User\Manager(), $this->groupManager, $this->config, $this->arrayCache]); + [new View(), new Manager(), $this->groupManager, $this->config, $this->arrayCache]); $this->util->expects($this->any()) ->method('getUidAndFilename') ->willReturnCallback(function ($path) { @@ -200,7 +201,7 @@ protected function setUp() { protected function buildMockModule() { $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll']) + ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser']) ->getMock(); $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); @@ -543,7 +544,7 @@ public function testGetHeader($path, $strippedPathExists, $strippedPath) { ->setConstructorArgs( [ new View(), - new \OC\User\Manager(), + new Manager(), $this->groupManager, $this->config, $this->arrayCache @@ -608,7 +609,7 @@ public function testGetHeaderAddLegacyModule($header, $isEncrypted, $expected) { ->disableOriginalConstructor()->getMock(); $util = $this->getMockBuilder('\OC\Encryption\Util') - ->setConstructorArgs([new View(), new \OC\User\Manager(), $this->groupManager, $this->config, $this->arrayCache]) + ->setConstructorArgs([new View(), new Manager(), $this->groupManager, $this->config, $this->arrayCache]) ->getMock(); $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') diff --git a/tests/lib/files/stream/encryption.php b/tests/lib/files/stream/encryption.php index afb31f2822d0..20a4100d2ff2 100644 --- a/tests/lib/files/stream/encryption.php +++ b/tests/lib/files/stream/encryption.php @@ -311,7 +311,7 @@ public function testWriteToNonSeekableStorage($testFile) { protected function buildMockModule() { $encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll']) + ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser']) ->getMock(); $encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');