@@ -5,6 +5,7 @@ declare(strict_types=1); | |||
namespace OC\Core\Command\Info; | |||
use OC\Files\ObjectStore\ObjectStoreStorage; | |||
use OC\Files\View; | |||
use OCA\Files_External\Config\ExternalMountPoint; | |||
use OCA\GroupFolders\Mount\GroupMountPoint; | |||
use OCP\Files\Folder; | |||
@@ -23,13 +24,16 @@ use Symfony\Component\Console\Output\OutputInterface; | |||
class File extends Command { | |||
private IL10N $l10n; | |||
private View $rootView; | |||
public function __construct( | |||
IFactory $l10nFactory, | |||
private FileUtils $fileUtils, | |||
private \OC\Encryption\Util $encryptionUtil | |||
) { | |||
$this->l10n = $l10nFactory->get("core"); | |||
parent::__construct(); | |||
$this->rootView = new View(); | |||
} | |||
protected function configure(): void { | |||
@@ -54,6 +58,14 @@ class File extends Command { | |||
$output->writeln(" mimetype: " . $node->getMimetype()); | |||
$output->writeln(" modified: " . (string)$this->l10n->l("datetime", $node->getMTime())); | |||
$output->writeln(" " . ($node->isEncrypted() ? "encrypted" : "not encrypted")); | |||
if ($node->isEncrypted()) { | |||
$keyPath = $this->encryptionUtil->getFileKeyDir('', $node->getPath()); | |||
if ($this->rootView->file_exists($keyPath)) { | |||
$output->writeln(" encryption key at: " . $keyPath); | |||
} else { | |||
$output->writeln(" <error>encryption key not found</error> should be located at: " . $keyPath); | |||
} | |||
} | |||
$output->writeln(" size: " . Util::humanFileSize($node->getSize())); | |||
$output->writeln(" etag: " . $node->getEtag()); | |||
if ($node instanceof Folder) { |
@@ -98,14 +98,14 @@ class Storage implements IStorage { | |||
*/ | |||
public function getFileKey($path, $keyId, $encryptionModuleId) { | |||
$realFile = $this->util->stripPartialFileExtension($path); | |||
$keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile); | |||
$keyDir = $this->util->getFileKeyDir($encryptionModuleId, $realFile); | |||
$key = $this->getKey($keyDir . $keyId)['key']; | |||
if ($key === '' && $realFile !== $path) { | |||
// Check if the part file has keys and use them, if no normal keys | |||
// exist. This is required to fix copyBetweenStorage() when we | |||
// rename a .part file over storage borders. | |||
$keyDir = $this->getFileKeyDir($encryptionModuleId, $path); | |||
$keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); | |||
$key = $this->getKey($keyDir . $keyId)['key']; | |||
} | |||
@@ -135,7 +135,7 @@ class Storage implements IStorage { | |||
* @inheritdoc | |||
*/ | |||
public function setFileKey($path, $keyId, $key, $encryptionModuleId) { | |||
$keyDir = $this->getFileKeyDir($encryptionModuleId, $path); | |||
$keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); | |||
return $this->setKey($keyDir . $keyId, [ | |||
'key' => base64_encode($key), | |||
]); | |||
@@ -177,7 +177,7 @@ class Storage implements IStorage { | |||
* @inheritdoc | |||
*/ | |||
public function deleteFileKey($path, $keyId, $encryptionModuleId) { | |||
$keyDir = $this->getFileKeyDir($encryptionModuleId, $path); | |||
$keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); | |||
return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId); | |||
} | |||
@@ -185,7 +185,7 @@ class Storage implements IStorage { | |||
* @inheritdoc | |||
*/ | |||
public function deleteAllFileKeys($path) { | |||
$keyDir = $this->getFileKeyDir('', $path); | |||
$keyDir = $this->util->getFileKeyDir('', $path); | |||
return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir); | |||
} | |||
@@ -355,26 +355,6 @@ class Storage implements IStorage { | |||
return false; | |||
} | |||
/** | |||
* get path to key folder for a given file | |||
* | |||
* @param string $encryptionModuleId | |||
* @param string $path path to the file, relative to data/ | |||
* @return string | |||
*/ | |||
private function getFileKeyDir($encryptionModuleId, $path) { | |||
[$owner, $filename] = $this->util->getUidAndFilename($path); | |||
// in case of system wide mount points the keys are stored directly in the data directory | |||
if ($this->util->isSystemWideMountPoint($filename, $owner)) { | |||
$keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/'; | |||
} else { | |||
$keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/'; | |||
} | |||
return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); | |||
} | |||
/** | |||
* move keys if a file was renamed | |||
* |
@@ -385,4 +385,25 @@ class Util { | |||
return $result; | |||
} | |||
/** | |||
* get path to key folder for a given file | |||
* | |||
* @param string $encryptionModuleId | |||
* @param string $path path to the file, relative to data/ | |||
* @return string | |||
*/ | |||
public function getFileKeyDir(string $encryptionModuleId, string $path): string { | |||
[$owner, $filename] = $this->getUidAndFilename($path); | |||
$root = $this->getKeyStorageRoot(); | |||
// in case of system-wide mount points the keys are stored directly in the data directory | |||
if ($this->isSystemWideMountPoint($filename, $owner)) { | |||
$keyPath = $root . '/' . '/files_encryption/keys' . $filename . '/'; | |||
} else { | |||
$keyPath = $root . '/' . $owner . '/files_encryption/keys' . $filename . '/'; | |||
} | |||
return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); | |||
} | |||
} |
@@ -53,6 +53,7 @@ class StorageTest extends TestCase { | |||
$this->util = $this->getMockBuilder('OC\Encryption\Util') | |||
->disableOriginalConstructor() | |||
->setMethodsExcept(['getFileKeyDir']) | |||
->getMock(); | |||
$this->view = $this->getMockBuilder(View::class) | |||
@@ -583,39 +584,6 @@ class StorageTest extends TestCase { | |||
$this->assertSame($expected, $args[0]); | |||
} | |||
/** | |||
* @dataProvider dataTestGetFileKeyDir | |||
* | |||
* @param bool $isSystemWideMountPoint | |||
* @param string $storageRoot | |||
* @param string $expected | |||
*/ | |||
public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected) { | |||
$path = '/user1/files/foo/bar.txt'; | |||
$owner = 'user1'; | |||
$relativePath = '/foo/bar.txt'; | |||
$this->invokePrivate($this->storage, 'root_dir', [$storageRoot]); | |||
$this->util->expects($this->once())->method('isSystemWideMountPoint') | |||
->willReturn($isSystemWideMountPoint); | |||
$this->util->expects($this->once())->method('getUidAndFilename') | |||
->with($path)->willReturn([$owner, $relativePath]); | |||
$this->assertSame($expected, | |||
$this->invokePrivate($this->storage, 'getFileKeyDir', ['OC_DEFAULT_MODULE', $path]) | |||
); | |||
} | |||
public function dataTestGetFileKeyDir() { | |||
return [ | |||
[false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
]; | |||
} | |||
/** | |||
* @dataProvider dataTestBackupUserKeys |
@@ -13,6 +13,7 @@ use Test\TestCase; | |||
class UtilTest extends TestCase { | |||
/** | |||
* block size will always be 8192 for a PHP stream | |||
* | |||
* @see https://bugs.php.net/bug.php?id=21641 | |||
*/ | |||
protected int $headerSize = 8192; | |||
@@ -205,4 +206,47 @@ class UtilTest extends TestCase { | |||
, []], | |||
]; | |||
} | |||
/** | |||
* @dataProvider dataTestGetFileKeyDir | |||
* | |||
* @param bool $isSystemWideMountPoint | |||
* @param string $storageRoot | |||
* @param string $expected | |||
*/ | |||
public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected) { | |||
$path = '/user1/files/foo/bar.txt'; | |||
$owner = 'user1'; | |||
$relativePath = '/foo/bar.txt'; | |||
$util = $this->getMockBuilder(Util::class) | |||
->onlyMethods(['isSystemWideMountPoint', 'getUidAndFilename', 'getKeyStorageRoot']) | |||
->setConstructorArgs([ | |||
$this->view, | |||
$this->userManager, | |||
$this->groupManager, | |||
$this->config | |||
]) | |||
->getMock(); | |||
$util->expects($this->once())->method('getKeyStorageRoot') | |||
->willReturn($storageRoot); | |||
$util->expects($this->once())->method('isSystemWideMountPoint') | |||
->willReturn($isSystemWideMountPoint); | |||
$util->expects($this->once())->method('getUidAndFilename') | |||
->with($path)->willReturn([$owner, $relativePath]); | |||
$this->assertSame($expected, | |||
$util->getFileKeyDir('OC_DEFAULT_MODULE', $path) | |||
); | |||
} | |||
public function dataTestGetFileKeyDir() { | |||
return [ | |||
[false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
[true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], | |||
]; | |||
} | |||
} |