Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor(Stream\Encryption): Migrate to strong types
Signed-off-by: provokateurin <[email protected]>
  • Loading branch information
provokateurin committed Oct 7, 2024
commit d1977b8d25030181ee7e115005f35b25654b1ecc
116 changes: 42 additions & 74 deletions lib/private/Files/Stream/Encryption.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
Expand All @@ -9,81 +11,42 @@

use Icewind\Streams\Wrapper;
use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException;
use OC\Encryption\File;
use OC\Encryption\Util;
use OC\Files\Storage\Storage;
use OCP\Encryption\IEncryptionModule;
use function is_array;
use function stream_context_create;

class Encryption extends Wrapper {
/** @var \OC\Encryption\Util */
protected $util;

/** @var \OC\Encryption\File */
protected $file;

/** @var \OCP\Encryption\IEncryptionModule */
protected $encryptionModule;

/** @var \OC\Files\Storage\Storage */
protected $storage;

/** @var \OC\Files\Storage\Wrapper\Encryption */
protected $encryptionStorage;

/** @var string */
protected $internalPath;

/** @var string */
protected $cache;

/** @var integer */
protected $size;

/** @var integer */
protected $position;

/** @var integer */
protected $unencryptedSize;

/** @var integer */
protected $headerSize;

/** @var integer */
protected $unencryptedBlockSize;

/** @var array */
protected $header;

/** @var string */
protected $fullPath;

/** @var bool */
protected $signed;

protected Util $util;
protected File $file;
protected IEncryptionModule $encryptionModule;
protected Storage $storage;
protected \OC\Files\Storage\Wrapper\Encryption $encryptionStorage;
protected string $internalPath;
protected string $cache;
protected ?int $size = null;
protected int $position;
protected ?int $unencryptedSize = null;
protected int $headerSize;
protected int $unencryptedBlockSize;
protected array $header;
protected string $fullPath;
protected bool $signed;
/**
* header data returned by the encryption module, will be written to the file
* in case of a write operation
*
* @var array
*/
protected $newHeader;

protected array $newHeader;
/**
* user who perform the read/write operation null for public access
*
* @var string
*/
protected $uid;

/** @var bool */
protected $readOnly;

/** @var bool */
protected $writeFlag;

/** @var array */
protected $expectedContextProperties;

/** @var bool */
protected $fileUpdated;
protected string $uid;
protected bool $readOnly;
protected bool $writeFlag;
protected array $expectedContextProperties;
protected bool $fileUpdated;

public function __construct() {
$this->expectedContextProperties = [
Expand Down Expand Up @@ -113,11 +76,11 @@ public function __construct() {
* @param string $fullPath relative to data/
* @param array $header
* @param string $uid
* @param \OCP\Encryption\IEncryptionModule $encryptionModule
* @param \OC\Files\Storage\Storage $storage
* @param IEncryptionModule $encryptionModule
* @param Storage $storage
* @param \OC\Files\Storage\Wrapper\Encryption $encStorage
* @param \OC\Encryption\Util $util
* @param \OC\Encryption\File $file
* @param Util $util
* @param File $file
* @param string $mode
* @param int|float $size
* @param int|float $unencryptedSize
Expand All @@ -128,19 +91,24 @@ public function __construct() {
*
* @throws \BadMethodCallException
*/
public static function wrap($source, $internalPath, $fullPath, array $header,
public static function wrap(
$source,
$internalPath,
$fullPath,
array $header,
$uid,
\OCP\Encryption\IEncryptionModule $encryptionModule,
\OC\Files\Storage\Storage $storage,
IEncryptionModule $encryptionModule,
Storage $storage,
\OC\Files\Storage\Wrapper\Encryption $encStorage,
\OC\Encryption\Util $util,
\OC\Encryption\File $file,
Util $util,
File $file,
$mode,
$size,
$unencryptedSize,
$headerSize,
$signed,
$wrapper = Encryption::class) {
$wrapper = Encryption::class,
) {
$context = stream_context_create([
'ocencryption' => [
'source' => $source,
Expand Down
64 changes: 38 additions & 26 deletions tests/lib/Files/Stream/EncryptionTest.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
Expand All @@ -7,27 +10,27 @@
namespace Test\Files\Stream;

use OC\Files\Cache\CacheEntry;
use OC\Files\Storage\Wrapper\Wrapper;
use OC\Files\View;
use OC\Memcache\ArrayCache;
use OCP\Encryption\IEncryptionModule;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Cache\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;

class EncryptionTest extends \Test\TestCase {
public const DEFAULT_WRAPPER = '\OC\Files\Stream\Encryption';

/** @var \OCP\Encryption\IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject */
private $encryptionModule;
private IEncryptionModule&MockObject $encryptionModule;

/**
* @param string $fileName
* @param string $mode
* @param integer $unencryptedSize
* @param class-string<Wrapper> $wrapper
* @return resource
*/
protected function getStream($fileName, $mode, $unencryptedSize, $wrapper = self::DEFAULT_WRAPPER, $unencryptedSizeOnClose = 0) {
protected function getStream(string $fileName, string $mode, int $unencryptedSize, string $wrapper = self::DEFAULT_WRAPPER, int $unencryptedSizeOnClose = 0) {
clearstatcache();
$size = filesize($fileName);
$source = fopen($fileName, $mode);
Expand Down Expand Up @@ -74,31 +77,47 @@ protected function getStream($fileName, $mode, $unencryptedSize, $wrapper = self
$cache->expects($this->any())->method('get')->willReturn($entry);
$cache->expects($this->any())->method('update')->with(5, ['encrypted' => 3, 'encryptedVersion' => 3, 'unencrypted_size' => $unencryptedSizeOnClose]);


return $wrapper::wrap($source, $internalPath,
$fullPath, $header, $uid, $this->encryptionModule, $storage, $encStorage,
$util, $file, $mode, $size, $unencryptedSize, 8192, $wrapper);
return $wrapper::wrap(
$source,
$internalPath,
$fullPath,
$header,
$uid,
$this->encryptionModule,
$storage,
$encStorage,
$util,
$file,
$mode,
$size,
$unencryptedSize,
8192,
true,
$wrapper,
);
}

/**
* @dataProvider dataProviderStreamOpen()
*/
public function testStreamOpen($isMasterKeyUsed,
public function testStreamOpen(
$isMasterKeyUsed,
$mode,
$fullPath,
$fileExists,
$expectedSharePath,
$expectedSize,
$expectedUnencryptedSize,
$expectedReadOnly): void {
$expectedReadOnly,
): void {
// build mocks
$encryptionModuleMock = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()->getMock();
$encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed);
$encryptionModuleMock->expects($this->once())
->method('getUnencryptedBlockSize')->willReturn(99);
$encryptionModuleMock->expects($this->once())
->method('begin')->willReturn(true);
->method('begin')->willReturn([]);

$storageMock = $this->getMockBuilder('\OC\Files\Storage\Storage')
->disableOriginalConstructor()->getMock();
Expand Down Expand Up @@ -152,6 +171,8 @@ public function testStreamOpen($isMasterKeyUsed,
$header->setValue($streamWrapper, []);
$header->setAccessible(false);
$this->invokePrivate($streamWrapper, 'signed', [true]);
$this->invokePrivate($streamWrapper, 'internalPath', [$fullPath]);
$this->invokePrivate($streamWrapper, 'uid', ['test']);

// call stream_open, that's the method we want to test
$dummyVar = 'foo';
Expand All @@ -160,23 +181,17 @@ public function testStreamOpen($isMasterKeyUsed,
// check internal properties
$size = $stream->getProperty('size');
$size->setAccessible(true);
$this->assertSame($expectedSize,
$size->getValue($streamWrapper)
);
$this->assertSame($expectedSize, $size->getValue($streamWrapper));
$size->setAccessible(false);

$unencryptedSize = $stream->getProperty('unencryptedSize');
$unencryptedSize->setAccessible(true);
$this->assertSame($expectedUnencryptedSize,
$unencryptedSize->getValue($streamWrapper)
);
$this->assertSame($expectedUnencryptedSize, $unencryptedSize->getValue($streamWrapper));
$unencryptedSize->setAccessible(false);

$readOnly = $stream->getProperty('readOnly');
$readOnly->setAccessible(true);
$this->assertSame($expectedReadOnly,
$readOnly->getValue($streamWrapper)
);
$this->assertSame($expectedReadOnly, $readOnly->getValue($streamWrapper));
$readOnly->setAccessible(false);
}

Expand Down Expand Up @@ -335,10 +350,7 @@ public function testWriteToNonSeekableStorage($testFile): void {
unlink($fileName);
}

/**
* @return \PHPUnit\Framework\MockObject\MockObject
*/
protected function buildMockModule() {
protected function buildMockModule(): IEncryptionModule&MockObject {
$encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()
->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
Expand Down