Skip to content

Commit 645d782

Browse files
authored
Merge pull request #66348 from nextcloud/backport/66284/stable31
[stable31] fix(encryption): Don't use ChunkingV2Plugin when server-side encryption is enabled
2 parents 0d12d32 + 4374691 commit 645d782

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎apps/dav/lib/Upload/ChunkingV2Plugin.php‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use InvalidArgumentException;
1313
use OC\Files\Filesystem;
1414
use OC\Files\ObjectStore\ObjectStoreStorage;
15+
use OC\Files\Storage\Wrapper\Encryption;
1516
use OC\Files\View;
1617
use OC\Memcache\Memcached;
1718
use OC\Memcache\Redis;
@@ -306,6 +307,9 @@ private function checkPrerequisites(bool $checkUploadMetadata = true): void {
306307
if (!$this->uploadFolder->getStorage()->instanceOfStorage(IChunkedFileWrite::class)) {
307308
throw new StorageInvalidException('Storage does not support chunked file writing');
308309
}
310+
if ($this->uploadFolder->getStorage()->instanceOfStorage(Encryption::class)) {
311+
throw new StorageInvalidException('Chunked file writing is not supported with server-side encryption');
312+
}
309313
if ($this->uploadFolder->getStorage()->instanceOfStorage(ObjectStoreStorage::class) && !$this->uploadFolder->getStorage()->getObjectStore() instanceof IObjectStoreMultiPartUpload) {
310314
throw new StorageInvalidException('Storage does not support multi part uploads');
311315
}

‎apps/dav/tests/unit/Upload/ChunkingV2PluginTest.php‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@
99

1010
namespace OCA\DAV\Tests\unit\Upload;
1111

12+
use OC\Files\ObjectStore\ObjectStoreStorage;
13+
use OC\Files\ObjectStore\S3;
14+
use OC\Files\Storage\Wrapper\Encryption;
15+
use OC\Memcache\Redis;
1216
use OCA\DAV\Connector\Sabre\Directory;
17+
use OCA\DAV\Connector\Sabre\File;
1318
use OCA\DAV\Upload\ChunkingV2Plugin;
1419
use OCA\DAV\Upload\FutureFile;
1520
use OCA\DAV\Upload\UploadFile;
21+
use OCA\DAV\Upload\UploadFolder;
22+
use OCP\Files\Storage\IChunkedFileWrite;
1623
use OCP\ICache;
1724
use OCP\ICacheFactory;
25+
use OCP\IConfig;
1826
use PHPUnit\Framework\MockObject\MockObject;
1927
use Sabre\DAV\Exception\MethodNotAllowed;
2028
use Sabre\DAV\Exception\NotFound;
@@ -102,4 +110,69 @@ public function testBeforeGetAllowsRegularNode(): void {
102110

103111
$this->assertTrue($this->plugin->forbiddenMethod($this->request));
104112
}
113+
114+
public static function dataAfterMkcolChunkedWrite(): array {
115+
return [
116+
'without encryption' => ['encrypted' => false],
117+
'with server-side encryption' => ['encrypted' => true],
118+
];
119+
}
120+
121+
/**
122+
* @dataProvider dataAfterMkcolChunkedWrite
123+
*/
124+
public function testAfterMkcolStartsChunkedWriteOnlyWithoutEncryption(bool $encrypted): void {
125+
$config = $this->createMock(IConfig::class);
126+
$config->method('getSystemValue')
127+
->willReturnCallback(static fn (string $key, mixed $default = '') => $key === 'memcache.distributed' ? Redis::class : $default);
128+
$this->overwriteService(IConfig::class, $config);
129+
130+
$cacheFactory = $this->createMock(ICacheFactory::class);
131+
$cacheFactory->method('createDistributed')->willReturn($this->createMock(Redis::class));
132+
$plugin = new ChunkingV2Plugin($cacheFactory);
133+
$plugin->initialize($this->server);
134+
135+
$storage = $this->createMock(ObjectStoreStorage::class);
136+
$storage->method('instanceOfStorage')
137+
->willReturnCallback(static fn (string $class): bool => match ($class) {
138+
IChunkedFileWrite::class, ObjectStoreStorage::class => true,
139+
Encryption::class => $encrypted,
140+
default => false,
141+
});
142+
$storage->method('getObjectStore')->willReturn($this->createMock(S3::class));
143+
$storage->expects($encrypted ? $this->never() : $this->once())
144+
->method('startChunkedWrite')
145+
->with('uploads/web-file-upload-id/.target')
146+
->willReturn('upload-token');
147+
148+
$targetFile = $this->createMock(File::class);
149+
$targetFile->method('getId')->willReturn(42);
150+
$targetFile->method('getInternalPath')->willReturn('uploads/web-file-upload-id/.target');
151+
$tempTarget = $this->createMock(UploadFile::class);
152+
$tempTarget->method('getFile')->willReturn($targetFile);
153+
154+
$uploadFolder = $this->createMock(UploadFolder::class);
155+
$uploadFolder->method('getName')->willReturn('web-file-upload-id');
156+
$uploadFolder->method('getStorage')->willReturn($storage);
157+
$uploadFolder->method('getChild')->with('.target')->willReturn($tempTarget);
158+
159+
$this->tree->method('getNodeForPath')
160+
->willReturnCallback(static function (string $path) use ($uploadFolder) {
161+
if ($path === 'uploads/admin/web-file-upload-id') {
162+
return $uploadFolder;
163+
}
164+
throw new NotFound();
165+
});
166+
$this->request->method('getPath')->willReturn('uploads/admin/web-file-upload-id');
167+
$this->request->method('getHeader')
168+
->with('Destination')
169+
->willReturn('http://localhost/remote.php/dav/files/admin/target.txt');
170+
$this->server->method('calculateUri')->willReturn('files/admin/target.txt');
171+
172+
$this->response->expects($encrypted ? $this->never() : $this->once())
173+
->method('setStatus')
174+
->with(201);
175+
176+
$this->assertTrue($plugin->afterMkcol($this->request, $this->response));
177+
}
105178
}

0 commit comments

Comments
 (0)