@@ -35,993 +35,993 @@ |
||
35 | 35 | use Test\Files\Storage\Storage; |
36 | 36 | |
37 | 37 | class EncryptionTest extends Storage { |
38 | - /** |
|
39 | - * block size will always be 8192 for a PHP stream |
|
40 | - * @see https://bugs.php.net/bug.php?id=21641 |
|
41 | - */ |
|
42 | - protected int $headerSize = 8192; |
|
43 | - private Temporary $sourceStorage; |
|
44 | - /** @var Encryption&MockObject */ |
|
45 | - protected $instance; |
|
46 | - private \OC\Encryption\Keys\Storage&MockObject $keyStore; |
|
47 | - private Util&MockObject $util; |
|
48 | - private \OC\Encryption\Manager&MockObject $encryptionManager; |
|
49 | - private IEncryptionModule&MockObject $encryptionModule; |
|
50 | - private Cache&MockObject $cache; |
|
51 | - private LoggerInterface&MockObject $logger; |
|
52 | - private File&MockObject $file; |
|
53 | - private MountPoint&MockObject $mount; |
|
54 | - private \OC\Files\Mount\Manager&MockObject $mountManager; |
|
55 | - private \OC\Group\Manager&MockObject $groupManager; |
|
56 | - private IConfig&MockObject $config; |
|
57 | - private ArrayCache&MockObject $arrayCache; |
|
58 | - /** dummy unencrypted size */ |
|
59 | - private int $dummySize = -1; |
|
60 | - |
|
61 | - protected function setUp(): void { |
|
62 | - parent::setUp(); |
|
63 | - |
|
64 | - $mockModule = $this->buildMockModule(); |
|
65 | - $this->encryptionManager = $this->getMockBuilder(\OC\Encryption\Manager::class) |
|
66 | - ->disableOriginalConstructor() |
|
67 | - ->onlyMethods(['getEncryptionModule', 'isEnabled']) |
|
68 | - ->getMock(); |
|
69 | - $this->encryptionManager->expects($this->any()) |
|
70 | - ->method('getEncryptionModule') |
|
71 | - ->willReturn($mockModule); |
|
72 | - |
|
73 | - $this->arrayCache = $this->createMock(ArrayCache::class); |
|
74 | - $this->config = $this->getMockBuilder(IConfig::class) |
|
75 | - ->disableOriginalConstructor() |
|
76 | - ->getMock(); |
|
77 | - $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') |
|
78 | - ->disableOriginalConstructor() |
|
79 | - ->getMock(); |
|
80 | - |
|
81 | - $this->util = $this->getMockBuilder(Util::class) |
|
82 | - ->onlyMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension']) |
|
83 | - ->setConstructorArgs([new View(), new Manager( |
|
84 | - $this->config, |
|
85 | - $this->createMock(ICacheFactory::class), |
|
86 | - $this->createMock(IEventDispatcher::class), |
|
87 | - $this->createMock(LoggerInterface::class), |
|
88 | - ), $this->groupManager, $this->config, $this->arrayCache]) |
|
89 | - ->getMock(); |
|
90 | - $this->util->expects($this->any()) |
|
91 | - ->method('getUidAndFilename') |
|
92 | - ->willReturnCallback(function ($path) { |
|
93 | - return ['user1', $path]; |
|
94 | - }); |
|
95 | - $this->util->expects($this->any()) |
|
96 | - ->method('stripPartialFileExtension') |
|
97 | - ->willReturnCallback(function ($path) { |
|
98 | - return $path; |
|
99 | - }); |
|
100 | - |
|
101 | - $this->file = $this->getMockBuilder(File::class) |
|
102 | - ->disableOriginalConstructor() |
|
103 | - ->onlyMethods(['getAccessList']) |
|
104 | - ->getMock(); |
|
105 | - $this->file->expects($this->any())->method('getAccessList')->willReturn([]); |
|
106 | - |
|
107 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
108 | - |
|
109 | - $this->sourceStorage = new Temporary([]); |
|
110 | - |
|
111 | - $this->keyStore = $this->createMock(\OC\Encryption\Keys\Storage::class); |
|
112 | - |
|
113 | - $this->mount = $this->getMockBuilder(MountPoint::class) |
|
114 | - ->disableOriginalConstructor() |
|
115 | - ->onlyMethods(['getOption']) |
|
116 | - ->getMock(); |
|
117 | - $this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) { |
|
118 | - if ($option === 'encrypt' && $default === true) { |
|
119 | - global $mockedMountPointEncryptionEnabled; |
|
120 | - if ($mockedMountPointEncryptionEnabled !== null) { |
|
121 | - return $mockedMountPointEncryptionEnabled; |
|
122 | - } |
|
123 | - } |
|
124 | - return true; |
|
125 | - }); |
|
126 | - |
|
127 | - $this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
128 | - ->disableOriginalConstructor()->getMock(); |
|
129 | - $this->cache->expects($this->any()) |
|
130 | - ->method('get') |
|
131 | - ->willReturnCallback(function ($path) { |
|
132 | - return ['encrypted' => false, 'path' => $path]; |
|
133 | - }); |
|
134 | - |
|
135 | - $this->mountManager = $this->createMock(\OC\Files\Mount\Manager::class); |
|
136 | - $this->mountManager->method('findByStorageId') |
|
137 | - ->willReturn([]); |
|
138 | - |
|
139 | - $this->instance = $this->getMockBuilder(Encryption::class) |
|
140 | - ->setConstructorArgs( |
|
141 | - [ |
|
142 | - [ |
|
143 | - 'storage' => $this->sourceStorage, |
|
144 | - 'root' => 'foo', |
|
145 | - 'mountPoint' => '/', |
|
146 | - 'mount' => $this->mount |
|
147 | - ], |
|
148 | - $this->encryptionManager, |
|
149 | - $this->util, |
|
150 | - $this->logger, |
|
151 | - $this->file, |
|
152 | - null, |
|
153 | - $this->keyStore, |
|
154 | - $this->mountManager, |
|
155 | - $this->arrayCache |
|
156 | - ] |
|
157 | - ) |
|
158 | - ->onlyMethods(['getMetaData', 'getCache', 'getEncryptionModule']) |
|
159 | - ->getMock(); |
|
160 | - |
|
161 | - $this->instance->expects($this->any()) |
|
162 | - ->method('getMetaData') |
|
163 | - ->willReturnCallback(function ($path) { |
|
164 | - return ['encrypted' => true, 'size' => $this->dummySize, 'path' => $path]; |
|
165 | - }); |
|
166 | - |
|
167 | - $this->instance->expects($this->any()) |
|
168 | - ->method('getCache') |
|
169 | - ->willReturn($this->cache); |
|
170 | - |
|
171 | - $this->instance->expects($this->any()) |
|
172 | - ->method('getEncryptionModule') |
|
173 | - ->willReturn($mockModule); |
|
174 | - } |
|
175 | - |
|
176 | - protected function buildMockModule(): IEncryptionModule&MockObject { |
|
177 | - $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') |
|
178 | - ->disableOriginalConstructor() |
|
179 | - ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) |
|
180 | - ->getMock(); |
|
181 | - |
|
182 | - $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); |
|
183 | - $this->encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module'); |
|
184 | - $this->encryptionModule->expects($this->any())->method('begin')->willReturn([]); |
|
185 | - $this->encryptionModule->expects($this->any())->method('end')->willReturn(''); |
|
186 | - $this->encryptionModule->expects($this->any())->method('encrypt')->willReturnArgument(0); |
|
187 | - $this->encryptionModule->expects($this->any())->method('decrypt')->willReturnArgument(0); |
|
188 | - $this->encryptionModule->expects($this->any())->method('update')->willReturn(true); |
|
189 | - $this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true); |
|
190 | - $this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192); |
|
191 | - $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true); |
|
192 | - $this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false); |
|
193 | - return $this->encryptionModule; |
|
194 | - } |
|
195 | - |
|
196 | - /** |
|
197 | - * |
|
198 | - * @param string $path |
|
199 | - * @param array $metaData |
|
200 | - * @param bool $encrypted |
|
201 | - * @param bool $unencryptedSizeSet |
|
202 | - * @param int $storedUnencryptedSize |
|
203 | - * @param array $expected |
|
204 | - */ |
|
205 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetMetaData')] |
|
206 | - public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void { |
|
207 | - $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
208 | - ->disableOriginalConstructor()->getMock(); |
|
209 | - |
|
210 | - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
211 | - ->disableOriginalConstructor()->getMock(); |
|
212 | - $cache->expects($this->any()) |
|
213 | - ->method('get') |
|
214 | - ->willReturnCallback( |
|
215 | - function ($path) use ($encrypted) { |
|
216 | - return new CacheEntry(['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1]); |
|
217 | - } |
|
218 | - ); |
|
219 | - |
|
220 | - $this->instance = $this->getMockBuilder(Encryption::class) |
|
221 | - ->setConstructorArgs( |
|
222 | - [ |
|
223 | - [ |
|
224 | - 'storage' => $sourceStorage, |
|
225 | - 'root' => 'foo', |
|
226 | - 'mountPoint' => '/', |
|
227 | - 'mount' => $this->mount |
|
228 | - ], |
|
229 | - $this->encryptionManager, |
|
230 | - $this->util, |
|
231 | - $this->logger, |
|
232 | - $this->file, |
|
233 | - null, |
|
234 | - $this->keyStore, |
|
235 | - $this->mountManager, |
|
236 | - $this->arrayCache, |
|
237 | - ] |
|
238 | - ) |
|
239 | - ->onlyMethods(['getCache', 'verifyUnencryptedSize']) |
|
240 | - ->getMock(); |
|
241 | - |
|
242 | - if ($unencryptedSizeSet) { |
|
243 | - $this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]); |
|
244 | - } |
|
245 | - |
|
246 | - $fileEntry = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
247 | - ->disableOriginalConstructor()->getMock(); |
|
248 | - $sourceStorage->expects($this->once())->method('getMetaData')->with($path) |
|
249 | - ->willReturn($metaData); |
|
250 | - $sourceStorage->expects($this->any()) |
|
251 | - ->method('getCache') |
|
252 | - ->with($path) |
|
253 | - ->willReturn($fileEntry); |
|
254 | - if ($metaData !== null) { |
|
255 | - $fileEntry->expects($this->any()) |
|
256 | - ->method('get') |
|
257 | - ->with($metaData['fileid']); |
|
258 | - } |
|
259 | - |
|
260 | - $this->instance->expects($this->any())->method('getCache')->willReturn($cache); |
|
261 | - if ($expected !== null) { |
|
262 | - $this->instance->expects($this->any())->method('verifyUnencryptedSize') |
|
263 | - ->with($path, 0)->willReturn($expected['size']); |
|
264 | - } |
|
265 | - |
|
266 | - $result = $this->instance->getMetaData($path); |
|
267 | - if (isset($expected['encrypted'])) { |
|
268 | - $this->assertSame($expected['encrypted'], (bool)$result['encrypted']); |
|
269 | - |
|
270 | - if (isset($expected['encryptedVersion'])) { |
|
271 | - $this->assertSame($expected['encryptedVersion'], $result['encryptedVersion']); |
|
272 | - } |
|
273 | - } |
|
274 | - |
|
275 | - if ($expected !== null) { |
|
276 | - $this->assertSame($expected['size'], $result['size']); |
|
277 | - } else { |
|
278 | - $this->assertSame(null, $result); |
|
279 | - } |
|
280 | - } |
|
281 | - |
|
282 | - public static function dataTestGetMetaData(): array { |
|
283 | - return [ |
|
284 | - ['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]], |
|
285 | - ['/test.txt', null, true, true, 12, null], |
|
286 | - ['/test.txt', ['size' => 42, 'encrypted' => 0, 'fileid' => 1], false, false, 12, ['size' => 42, 'encrypted' => false]], |
|
287 | - ['/test.txt', ['size' => 42, 'encrypted' => false, 'fileid' => 1], true, false, 12, ['size' => 12, 'encrypted' => true]] |
|
288 | - ]; |
|
289 | - } |
|
290 | - |
|
291 | - public function testFilesize(): void { |
|
292 | - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
293 | - ->disableOriginalConstructor()->getMock(); |
|
294 | - $cache->expects($this->any()) |
|
295 | - ->method('get') |
|
296 | - ->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1])); |
|
297 | - |
|
298 | - $this->instance = $this->getMockBuilder(Encryption::class) |
|
299 | - ->setConstructorArgs( |
|
300 | - [ |
|
301 | - [ |
|
302 | - 'storage' => $this->sourceStorage, |
|
303 | - 'root' => 'foo', |
|
304 | - 'mountPoint' => '/', |
|
305 | - 'mount' => $this->mount |
|
306 | - ], |
|
307 | - $this->encryptionManager, |
|
308 | - $this->util, |
|
309 | - $this->logger, |
|
310 | - $this->file, |
|
311 | - null, |
|
312 | - $this->keyStore, |
|
313 | - $this->mountManager, |
|
314 | - $this->arrayCache, |
|
315 | - ] |
|
316 | - ) |
|
317 | - ->onlyMethods(['getCache', 'verifyUnencryptedSize']) |
|
318 | - ->getMock(); |
|
319 | - |
|
320 | - $this->instance->expects($this->any())->method('getCache')->willReturn($cache); |
|
321 | - $this->instance->expects($this->any())->method('verifyUnencryptedSize') |
|
322 | - ->willReturn(42); |
|
323 | - |
|
324 | - |
|
325 | - $this->assertSame(42, |
|
326 | - $this->instance->filesize('/test.txt') |
|
327 | - ); |
|
328 | - } |
|
329 | - |
|
330 | - /** |
|
331 | - * |
|
332 | - * @param int $encryptedSize |
|
333 | - * @param int $unencryptedSize |
|
334 | - * @param bool $failure |
|
335 | - * @param int $expected |
|
336 | - */ |
|
337 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestVerifyUnencryptedSize')] |
|
338 | - public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void { |
|
339 | - $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
340 | - ->disableOriginalConstructor()->getMock(); |
|
341 | - |
|
342 | - $this->instance = $this->getMockBuilder(Encryption::class) |
|
343 | - ->setConstructorArgs( |
|
344 | - [ |
|
345 | - [ |
|
346 | - 'storage' => $sourceStorage, |
|
347 | - 'root' => 'foo', |
|
348 | - 'mountPoint' => '/', |
|
349 | - 'mount' => $this->mount |
|
350 | - ], |
|
351 | - $this->encryptionManager, |
|
352 | - $this->util, |
|
353 | - $this->logger, |
|
354 | - $this->file, |
|
355 | - null, |
|
356 | - $this->keyStore, |
|
357 | - $this->mountManager, |
|
358 | - $this->arrayCache, |
|
359 | - ] |
|
360 | - ) |
|
361 | - ->onlyMethods(['fixUnencryptedSize']) |
|
362 | - ->getMock(); |
|
363 | - |
|
364 | - $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize); |
|
365 | - |
|
366 | - $this->instance->expects($this->any())->method('fixUnencryptedSize') |
|
367 | - ->with('/test.txt', $encryptedSize, $unencryptedSize) |
|
368 | - ->willReturnCallback( |
|
369 | - function () use ($failure, $expected) { |
|
370 | - if ($failure) { |
|
371 | - throw new Exception(); |
|
372 | - } else { |
|
373 | - return $expected; |
|
374 | - } |
|
375 | - } |
|
376 | - ); |
|
377 | - |
|
378 | - $this->assertSame( |
|
379 | - $expected, |
|
380 | - $this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize]) |
|
381 | - ); |
|
382 | - } |
|
383 | - |
|
384 | - public static function dataTestVerifyUnencryptedSize(): array { |
|
385 | - return [ |
|
386 | - [120, 80, false, 80], |
|
387 | - [120, 120, false, 80], |
|
388 | - [120, -1, false, 80], |
|
389 | - [120, -1, true, -1] |
|
390 | - ]; |
|
391 | - } |
|
392 | - |
|
393 | - /** |
|
394 | - * |
|
395 | - * @param string $source |
|
396 | - * @param string $target |
|
397 | - * @param $encryptionEnabled |
|
398 | - * @param boolean $renameKeysReturn |
|
399 | - */ |
|
400 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyAndRename')] |
|
401 | - public function testRename($source, |
|
402 | - $target, |
|
403 | - $encryptionEnabled, |
|
404 | - $renameKeysReturn): void { |
|
405 | - if ($encryptionEnabled) { |
|
406 | - $this->keyStore |
|
407 | - ->expects($this->once()) |
|
408 | - ->method('renameKeys') |
|
409 | - ->willReturn($renameKeysReturn); |
|
410 | - } else { |
|
411 | - $this->keyStore |
|
412 | - ->expects($this->never())->method('renameKeys'); |
|
413 | - } |
|
414 | - $this->util->expects($this->any()) |
|
415 | - ->method('isFile')->willReturn(true); |
|
416 | - $this->encryptionManager->expects($this->once()) |
|
417 | - ->method('isEnabled')->willReturn($encryptionEnabled); |
|
418 | - |
|
419 | - $this->instance->mkdir($source); |
|
420 | - $this->instance->mkdir(dirname($target)); |
|
421 | - $this->instance->rename($source, $target); |
|
422 | - } |
|
423 | - |
|
424 | - public function testCopyEncryption(): void { |
|
425 | - $this->instance->file_put_contents('source.txt', 'bar'); |
|
426 | - $this->instance->copy('source.txt', 'target.txt'); |
|
427 | - $this->assertSame('bar', $this->instance->file_get_contents('target.txt')); |
|
428 | - $targetMeta = $this->instance->getMetaData('target.txt'); |
|
429 | - $sourceMeta = $this->instance->getMetaData('source.txt'); |
|
430 | - $this->assertSame($sourceMeta['encrypted'], $targetMeta['encrypted']); |
|
431 | - $this->assertSame($sourceMeta['size'], $targetMeta['size']); |
|
432 | - } |
|
433 | - |
|
434 | - /** |
|
435 | - * data provider for testCopyTesting() and dataTestCopyAndRename() |
|
436 | - * |
|
437 | - * @return array |
|
438 | - */ |
|
439 | - public static function dataTestCopyAndRename(): array { |
|
440 | - return [ |
|
441 | - ['source', 'target', true, false, false], |
|
442 | - ['source', 'target', true, true, false], |
|
443 | - ['source', '/subFolder/target', true, false, false], |
|
444 | - ['source', '/subFolder/target', true, true, true], |
|
445 | - ['source', '/subFolder/target', false, true, false], |
|
446 | - ]; |
|
447 | - } |
|
448 | - |
|
449 | - public function testIsLocal(): void { |
|
450 | - $this->encryptionManager->expects($this->once()) |
|
451 | - ->method('isEnabled')->willReturn(true); |
|
452 | - $this->assertFalse($this->instance->isLocal()); |
|
453 | - } |
|
454 | - |
|
455 | - /** |
|
456 | - * |
|
457 | - * @param string $path |
|
458 | - * @param boolean $rmdirResult |
|
459 | - * @param boolean $isExcluded |
|
460 | - * @param boolean $encryptionEnabled |
|
461 | - */ |
|
462 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestRmdir')] |
|
463 | - public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void { |
|
464 | - $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
465 | - ->disableOriginalConstructor()->getMock(); |
|
466 | - |
|
467 | - $util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock(); |
|
468 | - |
|
469 | - $sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult); |
|
470 | - $util->expects($this->any())->method('isExcluded')->willReturn($isExcluded); |
|
471 | - $this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled); |
|
472 | - |
|
473 | - $encryptionStorage = new Encryption( |
|
474 | - [ |
|
475 | - 'storage' => $sourceStorage, |
|
476 | - 'root' => 'foo', |
|
477 | - 'mountPoint' => '/mountPoint', |
|
478 | - 'mount' => $this->mount |
|
479 | - ], |
|
480 | - $this->encryptionManager, |
|
481 | - $util, |
|
482 | - $this->logger, |
|
483 | - $this->file, |
|
484 | - null, |
|
485 | - $this->keyStore, |
|
486 | - $this->mountManager, |
|
487 | - $this->arrayCache, |
|
488 | - ); |
|
489 | - |
|
490 | - |
|
491 | - if ($rmdirResult === true && $isExcluded === false && $encryptionEnabled === true) { |
|
492 | - $this->keyStore->expects($this->once())->method('deleteAllFileKeys')->with('/mountPoint' . $path); |
|
493 | - } else { |
|
494 | - $this->keyStore->expects($this->never())->method('deleteAllFileKeys'); |
|
495 | - } |
|
496 | - |
|
497 | - $encryptionStorage->rmdir($path); |
|
498 | - } |
|
499 | - |
|
500 | - public static function dataTestRmdir(): array { |
|
501 | - return [ |
|
502 | - ['/file.txt', true, true, true], |
|
503 | - ['/file.txt', false, true, true], |
|
504 | - ['/file.txt', true, false, true], |
|
505 | - ['/file.txt', false, false, true], |
|
506 | - ['/file.txt', true, true, false], |
|
507 | - ['/file.txt', false, true, false], |
|
508 | - ['/file.txt', true, false, false], |
|
509 | - ['/file.txt', false, false, false], |
|
510 | - ]; |
|
511 | - } |
|
512 | - |
|
513 | - /** |
|
514 | - * |
|
515 | - * @param boolean $excluded |
|
516 | - * @param boolean $expected |
|
517 | - */ |
|
518 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyKeys')] |
|
519 | - public function testCopyKeys($excluded, $expected): void { |
|
520 | - $this->util->expects($this->once()) |
|
521 | - ->method('isExcluded') |
|
522 | - ->willReturn($excluded); |
|
523 | - |
|
524 | - if ($excluded) { |
|
525 | - $this->keyStore->expects($this->never())->method('copyKeys'); |
|
526 | - } else { |
|
527 | - $this->keyStore->expects($this->once())->method('copyKeys')->willReturn(true); |
|
528 | - } |
|
529 | - |
|
530 | - $this->assertSame($expected, |
|
531 | - self::invokePrivate($this->instance, 'copyKeys', ['/source', '/target']) |
|
532 | - ); |
|
533 | - } |
|
534 | - |
|
535 | - public static function dataTestCopyKeys(): array { |
|
536 | - return [ |
|
537 | - [true, false], |
|
538 | - [false, true], |
|
539 | - ]; |
|
540 | - } |
|
541 | - |
|
542 | - /** |
|
543 | - * |
|
544 | - * @param string $path |
|
545 | - * @param bool $strippedPathExists |
|
546 | - * @param string $strippedPath |
|
547 | - */ |
|
548 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeader')] |
|
549 | - public function testGetHeader($path, $strippedPathExists, $strippedPath): void { |
|
550 | - $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
551 | - ->disableOriginalConstructor()->getMock(); |
|
552 | - |
|
553 | - $util = $this->getMockBuilder('\OC\Encryption\Util') |
|
554 | - ->setConstructorArgs( |
|
555 | - [ |
|
556 | - new View(), |
|
557 | - new Manager( |
|
558 | - $this->config, |
|
559 | - $this->createMock(ICacheFactory::class), |
|
560 | - $this->createMock(IEventDispatcher::class), |
|
561 | - $this->createMock(LoggerInterface::class), |
|
562 | - ), |
|
563 | - $this->groupManager, |
|
564 | - $this->config, |
|
565 | - $this->arrayCache |
|
566 | - ] |
|
567 | - )->getMock(); |
|
568 | - |
|
569 | - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
570 | - ->disableOriginalConstructor()->getMock(); |
|
571 | - $cache->expects($this->any()) |
|
572 | - ->method('get') |
|
573 | - ->willReturnCallback(function ($path) { |
|
574 | - return ['encrypted' => true, 'path' => $path]; |
|
575 | - }); |
|
576 | - |
|
577 | - $instance = $this->getMockBuilder(Encryption::class) |
|
578 | - ->setConstructorArgs( |
|
579 | - [ |
|
580 | - [ |
|
581 | - 'storage' => $sourceStorage, |
|
582 | - 'root' => 'foo', |
|
583 | - 'mountPoint' => '/', |
|
584 | - 'mount' => $this->mount |
|
585 | - ], |
|
586 | - $this->encryptionManager, |
|
587 | - $util, |
|
588 | - $this->logger, |
|
589 | - $this->file, |
|
590 | - null, |
|
591 | - $this->keyStore, |
|
592 | - $this->mountManager, |
|
593 | - $this->arrayCache, |
|
594 | - ] |
|
595 | - ) |
|
596 | - ->onlyMethods(['getCache', 'readFirstBlock']) |
|
597 | - ->getMock(); |
|
598 | - |
|
599 | - $instance->method('getCache')->willReturn($cache); |
|
600 | - |
|
601 | - $util->method('parseRawHeader') |
|
602 | - ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']); |
|
603 | - |
|
604 | - if ($strippedPathExists) { |
|
605 | - $instance->method('readFirstBlock') |
|
606 | - ->with($strippedPath)->willReturn(''); |
|
607 | - } else { |
|
608 | - $instance->method('readFirstBlock') |
|
609 | - ->with($path)->willReturn(''); |
|
610 | - } |
|
611 | - |
|
612 | - $util->expects($this->once())->method('stripPartialFileExtension') |
|
613 | - ->with($path)->willReturn($strippedPath); |
|
614 | - $sourceStorage->expects($this->once()) |
|
615 | - ->method('is_file') |
|
616 | - ->with($strippedPath) |
|
617 | - ->willReturn($strippedPathExists); |
|
618 | - |
|
619 | - $this->invokePrivate($instance, 'getHeader', [$path]); |
|
620 | - } |
|
621 | - |
|
622 | - public static function dataTestGetHeader(): array { |
|
623 | - return [ |
|
624 | - ['/foo/bar.txt', false, '/foo/bar.txt'], |
|
625 | - ['/foo/bar.txt.part', false, '/foo/bar.txt'], |
|
626 | - ['/foo/bar.txt.ocTransferId7437493.part', false, '/foo/bar.txt'], |
|
627 | - ['/foo/bar.txt.part', true, '/foo/bar.txt'], |
|
628 | - ['/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'], |
|
629 | - ]; |
|
630 | - } |
|
631 | - |
|
632 | - /** |
|
633 | - * test if getHeader adds the default module correctly to the header for |
|
634 | - * legacy files |
|
635 | - */ |
|
636 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeaderAddLegacyModule')] |
|
637 | - public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void { |
|
638 | - $sourceStorage = $this->getMockBuilder(\OC\Files\Storage\Storage::class) |
|
639 | - ->disableOriginalConstructor()->getMock(); |
|
640 | - |
|
641 | - $sourceStorage->expects($this->once()) |
|
642 | - ->method('is_file') |
|
643 | - ->with('test.txt') |
|
644 | - ->willReturn($strippedPathExists); |
|
645 | - |
|
646 | - $util = $this->getMockBuilder(Util::class) |
|
647 | - ->onlyMethods(['stripPartialFileExtension', 'parseRawHeader']) |
|
648 | - ->setConstructorArgs([new View(), new Manager( |
|
649 | - $this->config, |
|
650 | - $this->createMock(ICacheFactory::class), |
|
651 | - $this->createMock(IEventDispatcher::class), |
|
652 | - $this->createMock(LoggerInterface::class), |
|
653 | - ), $this->groupManager, $this->config, $this->arrayCache]) |
|
654 | - ->getMock(); |
|
655 | - $util->expects($this->any()) |
|
656 | - ->method('stripPartialFileExtension') |
|
657 | - ->willReturnCallback(function ($path) { |
|
658 | - return $path; |
|
659 | - }); |
|
660 | - |
|
661 | - $cache = $this->createMock(Cache::class); |
|
662 | - $cache->expects($this->any()) |
|
663 | - ->method('get') |
|
664 | - ->willReturnCallback(function ($path) use ($isEncrypted) { |
|
665 | - return ['encrypted' => $isEncrypted, 'path' => $path]; |
|
666 | - }); |
|
667 | - |
|
668 | - $instance = $this->getMockBuilder(Encryption::class) |
|
669 | - ->setConstructorArgs( |
|
670 | - [ |
|
671 | - [ |
|
672 | - 'storage' => $sourceStorage, |
|
673 | - 'root' => 'foo', |
|
674 | - 'mountPoint' => '/', |
|
675 | - 'mount' => $this->mount |
|
676 | - ], |
|
677 | - $this->encryptionManager, |
|
678 | - $util, |
|
679 | - $this->logger, |
|
680 | - $this->file, |
|
681 | - null, |
|
682 | - $this->keyStore, |
|
683 | - $this->mountManager, |
|
684 | - $this->arrayCache, |
|
685 | - ] |
|
686 | - ) |
|
687 | - ->onlyMethods(['readFirstBlock', 'getCache']) |
|
688 | - ->getMock(); |
|
689 | - |
|
690 | - $instance->method('readFirstBlock')->willReturn(''); |
|
691 | - |
|
692 | - $util->method(('parseRawHeader'))->willReturn($header); |
|
693 | - $instance->method('getCache')->willReturn($cache); |
|
694 | - |
|
695 | - $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']); |
|
696 | - $this->assertSameSize($expected, $result); |
|
697 | - foreach ($result as $key => $value) { |
|
698 | - $this->assertArrayHasKey($key, $expected); |
|
699 | - $this->assertSame($expected[$key], $value); |
|
700 | - } |
|
701 | - } |
|
702 | - |
|
703 | - public static function dataTestGetHeaderAddLegacyModule(): array { |
|
704 | - return [ |
|
705 | - [['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], |
|
706 | - [[], true, false, []], |
|
707 | - [[], true, true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], |
|
708 | - [[], false, true, []], |
|
709 | - ]; |
|
710 | - } |
|
711 | - |
|
712 | - public static function dataCopyBetweenStorage(): array { |
|
713 | - return [ |
|
714 | - [true, true, true], |
|
715 | - [true, false, false], |
|
716 | - [false, true, false], |
|
717 | - [false, false, false], |
|
718 | - ]; |
|
719 | - } |
|
720 | - |
|
721 | - public function testCopyBetweenStorageMinimumEncryptedVersion(): void { |
|
722 | - $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); |
|
723 | - |
|
724 | - $sourceInternalPath = $targetInternalPath = 'file.txt'; |
|
725 | - $preserveMtime = $isRename = false; |
|
726 | - |
|
727 | - $storage2->expects($this->any()) |
|
728 | - ->method('fopen') |
|
729 | - ->willReturnCallback(function ($path, $mode) { |
|
730 | - $temp = Server::get(ITempManager::class); |
|
731 | - return fopen($temp->getTemporaryFile(), $mode); |
|
732 | - }); |
|
733 | - $storage2->method('getId') |
|
734 | - ->willReturn('stroage2'); |
|
735 | - $cache = $this->createMock(ICache::class); |
|
736 | - $cache->expects($this->once()) |
|
737 | - ->method('get') |
|
738 | - ->with($sourceInternalPath) |
|
739 | - ->willReturn(['encryptedVersion' => 0]); |
|
740 | - $storage2->expects($this->once()) |
|
741 | - ->method('getCache') |
|
742 | - ->willReturn($cache); |
|
743 | - $this->encryptionManager->expects($this->any()) |
|
744 | - ->method('isEnabled') |
|
745 | - ->willReturn(true); |
|
746 | - global $mockedMountPointEncryptionEnabled; |
|
747 | - $mockedMountPointEncryptionEnabled = true; |
|
748 | - |
|
749 | - $expectedCachePut = [ |
|
750 | - 'encrypted' => true, |
|
751 | - ]; |
|
752 | - $expectedCachePut['encryptedVersion'] = 1; |
|
753 | - |
|
754 | - $this->cache->expects($this->once()) |
|
755 | - ->method('put') |
|
756 | - ->with($sourceInternalPath, $expectedCachePut); |
|
757 | - |
|
758 | - $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]); |
|
759 | - |
|
760 | - $this->assertFalse(false); |
|
761 | - } |
|
762 | - |
|
763 | - /** |
|
764 | - * |
|
765 | - * @param bool $encryptionEnabled |
|
766 | - * @param bool $mountPointEncryptionEnabled |
|
767 | - * @param bool $expectedEncrypted |
|
768 | - */ |
|
769 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataCopyBetweenStorage')] |
|
770 | - public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void { |
|
771 | - $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); |
|
772 | - |
|
773 | - $sourceInternalPath = $targetInternalPath = 'file.txt'; |
|
774 | - $preserveMtime = $isRename = false; |
|
775 | - |
|
776 | - $storage2->expects($this->any()) |
|
777 | - ->method('fopen') |
|
778 | - ->willReturnCallback(function ($path, $mode) { |
|
779 | - $temp = Server::get(ITempManager::class); |
|
780 | - return fopen($temp->getTemporaryFile(), $mode); |
|
781 | - }); |
|
782 | - $storage2->method('getId') |
|
783 | - ->willReturn('stroage2'); |
|
784 | - if ($expectedEncrypted) { |
|
785 | - $cache = $this->createMock(ICache::class); |
|
786 | - $cache->expects($this->once()) |
|
787 | - ->method('get') |
|
788 | - ->with($sourceInternalPath) |
|
789 | - ->willReturn(['encryptedVersion' => 12345]); |
|
790 | - $storage2->expects($this->once()) |
|
791 | - ->method('getCache') |
|
792 | - ->willReturn($cache); |
|
793 | - } |
|
794 | - $this->encryptionManager->expects($this->any()) |
|
795 | - ->method('isEnabled') |
|
796 | - ->willReturn($encryptionEnabled); |
|
797 | - // FIXME can not overwrite the return after definition |
|
798 | - // $this->mount->expects($this->at(0)) |
|
799 | - // ->method('getOption') |
|
800 | - // ->with('encrypt', true) |
|
801 | - // ->willReturn($mountPointEncryptionEnabled); |
|
802 | - global $mockedMountPointEncryptionEnabled; |
|
803 | - $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled; |
|
804 | - |
|
805 | - $expectedCachePut = [ |
|
806 | - 'encrypted' => $expectedEncrypted, |
|
807 | - ]; |
|
808 | - if ($expectedEncrypted === true) { |
|
809 | - $expectedCachePut['encryptedVersion'] = 1; |
|
810 | - } |
|
811 | - |
|
812 | - $this->arrayCache->expects($this->never())->method('set'); |
|
813 | - |
|
814 | - $this->cache->expects($this->once()) |
|
815 | - ->method('put') |
|
816 | - ->with($sourceInternalPath, $expectedCachePut); |
|
817 | - |
|
818 | - $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]); |
|
819 | - |
|
820 | - $this->assertFalse(false); |
|
821 | - } |
|
822 | - |
|
823 | - /** |
|
824 | - * |
|
825 | - * @param string $sourceInternalPath |
|
826 | - * @param string $targetInternalPath |
|
827 | - * @param bool $copyResult |
|
828 | - * @param bool $encrypted |
|
829 | - */ |
|
830 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyBetweenStorageVersions')] |
|
831 | - public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void { |
|
832 | - $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class); |
|
833 | - |
|
834 | - $targetStorage = $this->createMock(\OC\Files\Storage\Storage::class); |
|
835 | - |
|
836 | - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
837 | - ->disableOriginalConstructor()->getMock(); |
|
838 | - |
|
839 | - $mountPoint = '/mountPoint'; |
|
840 | - |
|
841 | - /** @var Encryption |MockObject $instance */ |
|
842 | - $instance = $this->getMockBuilder(Encryption::class) |
|
843 | - ->setConstructorArgs( |
|
844 | - [ |
|
845 | - [ |
|
846 | - 'storage' => $targetStorage, |
|
847 | - 'root' => 'foo', |
|
848 | - 'mountPoint' => $mountPoint, |
|
849 | - 'mount' => $this->mount |
|
850 | - ], |
|
851 | - $this->encryptionManager, |
|
852 | - $this->util, |
|
853 | - $this->logger, |
|
854 | - $this->file, |
|
855 | - null, |
|
856 | - $this->keyStore, |
|
857 | - $this->mountManager, |
|
858 | - $this->arrayCache |
|
859 | - ] |
|
860 | - ) |
|
861 | - ->onlyMethods(['updateUnencryptedSize', 'getCache']) |
|
862 | - ->getMock(); |
|
863 | - |
|
864 | - $targetStorage->expects($this->once())->method('copyFromStorage') |
|
865 | - ->with($sourceStorage, $sourceInternalPath, $targetInternalPath) |
|
866 | - ->willReturn($copyResult); |
|
867 | - |
|
868 | - $instance->expects($this->any())->method('getCache') |
|
869 | - ->willReturn($cache); |
|
870 | - |
|
871 | - $this->arrayCache->expects($this->once())->method('set') |
|
872 | - ->with('encryption_copy_version_' . $sourceInternalPath, true); |
|
873 | - |
|
874 | - if ($copyResult) { |
|
875 | - $cache->expects($this->once())->method('get') |
|
876 | - ->with($sourceInternalPath) |
|
877 | - ->willReturn(new CacheEntry(['encrypted' => $encrypted, 'size' => 42])); |
|
878 | - if ($encrypted) { |
|
879 | - $instance->expects($this->once())->method('updateUnencryptedSize') |
|
880 | - ->with($mountPoint . $targetInternalPath, 42); |
|
881 | - } else { |
|
882 | - $instance->expects($this->never())->method('updateUnencryptedSize'); |
|
883 | - } |
|
884 | - } else { |
|
885 | - $instance->expects($this->never())->method('updateUnencryptedSize'); |
|
886 | - } |
|
887 | - |
|
888 | - $result = $this->invokePrivate( |
|
889 | - $instance, |
|
890 | - 'copyBetweenStorage', |
|
891 | - [ |
|
892 | - $sourceStorage, |
|
893 | - $sourceInternalPath, |
|
894 | - $targetInternalPath, |
|
895 | - false, |
|
896 | - false |
|
897 | - ] |
|
898 | - ); |
|
899 | - |
|
900 | - $this->assertSame($copyResult, $result); |
|
901 | - } |
|
902 | - |
|
903 | - public static function dataTestCopyBetweenStorageVersions(): array { |
|
904 | - return [ |
|
905 | - ['/files/foo.txt', '/files_versions/foo.txt.768743', true, true], |
|
906 | - ['/files/foo.txt', '/files_versions/foo.txt.768743', true, false], |
|
907 | - ['/files/foo.txt', '/files_versions/foo.txt.768743', false, true], |
|
908 | - ['/files/foo.txt', '/files_versions/foo.txt.768743', false, false], |
|
909 | - ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, true], |
|
910 | - ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, false], |
|
911 | - ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, true], |
|
912 | - ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, false], |
|
913 | - |
|
914 | - ]; |
|
915 | - } |
|
916 | - |
|
917 | - /** |
|
918 | - * @param string $path |
|
919 | - * @param bool $expected |
|
920 | - */ |
|
921 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsVersion')] |
|
922 | - public function testIsVersion($path, $expected): void { |
|
923 | - $this->assertSame($expected, |
|
924 | - $this->invokePrivate($this->instance, 'isVersion', [$path]) |
|
925 | - ); |
|
926 | - } |
|
927 | - |
|
928 | - public static function dataTestIsVersion(): array { |
|
929 | - return [ |
|
930 | - ['files_versions/foo', true], |
|
931 | - ['/files_versions/foo', true], |
|
932 | - ['//files_versions/foo', true], |
|
933 | - ['files/versions/foo', false], |
|
934 | - ['files/files_versions/foo', false], |
|
935 | - ['files_versions_test/foo', false], |
|
936 | - ]; |
|
937 | - } |
|
938 | - |
|
939 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')] |
|
940 | - public function testShouldEncrypt( |
|
941 | - bool $encryptionEnabled, |
|
942 | - bool $encryptMountPoint, |
|
943 | - ?bool $encryptionModule, |
|
944 | - bool $encryptionModuleShouldEncrypt, |
|
945 | - bool $expected, |
|
946 | - ): void { |
|
947 | - $encryptionManager = $this->createMock(\OC\Encryption\Manager::class); |
|
948 | - $util = $this->createMock(Util::class); |
|
949 | - $fileHelper = $this->createMock(IFile::class); |
|
950 | - $keyStorage = $this->createMock(IStorage::class); |
|
951 | - $mountManager = $this->createMock(\OC\Files\Mount\Manager::class); |
|
952 | - $mount = $this->createMock(IMountPoint::class); |
|
953 | - $arrayCache = $this->createMock(ArrayCache::class); |
|
954 | - $path = '/welcome.txt'; |
|
955 | - $fullPath = 'admin/files/welcome.txt'; |
|
956 | - $defaultEncryptionModule = $this->createMock(IEncryptionModule::class); |
|
957 | - |
|
958 | - $wrapper = $this->getMockBuilder(Encryption::class) |
|
959 | - ->setConstructorArgs( |
|
960 | - [ |
|
961 | - ['mountPoint' => '', 'mount' => $mount, 'storage' => ''], |
|
962 | - $encryptionManager, |
|
963 | - $util, |
|
964 | - $this->logger, |
|
965 | - $fileHelper, |
|
966 | - null, |
|
967 | - $keyStorage, |
|
968 | - $mountManager, |
|
969 | - $arrayCache |
|
970 | - ] |
|
971 | - ) |
|
972 | - ->onlyMethods(['getFullPath', 'getEncryptionModule']) |
|
973 | - ->getMock(); |
|
974 | - |
|
975 | - $encryptionManager->method('isEnabled')->willReturn($encryptionEnabled); |
|
976 | - |
|
977 | - if ($encryptionModule === true) { |
|
978 | - /** @var IEncryptionModule|MockObject $encryptionModule */ |
|
979 | - $encryptionModule = $this->createMock(IEncryptionModule::class); |
|
980 | - } |
|
981 | - |
|
982 | - $wrapper->method('getFullPath')->with($path)->willReturn($fullPath); |
|
983 | - $wrapper->expects(($encryptionEnabled && $encryptMountPoint) ? $this->once() : $this->never()) |
|
984 | - ->method('getEncryptionModule') |
|
985 | - ->with($fullPath) |
|
986 | - ->willReturnCallback( |
|
987 | - function () use ($encryptionModule) { |
|
988 | - if ($encryptionModule === false) { |
|
989 | - throw new ModuleDoesNotExistsException(); |
|
990 | - } |
|
991 | - return $encryptionModule; |
|
992 | - } |
|
993 | - ); |
|
994 | - $mount->expects($encryptionEnabled ? $this->once() : $this->never()) |
|
995 | - ->method('getOption')->with('encrypt', true) |
|
996 | - ->willReturn($encryptMountPoint); |
|
997 | - |
|
998 | - if ($encryptionModule !== null && $encryptionModule !== false) { |
|
999 | - $encryptionModule |
|
1000 | - ->method('shouldEncrypt') |
|
1001 | - ->with($fullPath) |
|
1002 | - ->willReturn($encryptionModuleShouldEncrypt); |
|
1003 | - } |
|
1004 | - |
|
1005 | - if ($encryptionModule === null) { |
|
1006 | - $encryptionManager->expects($this->once()) |
|
1007 | - ->method('getEncryptionModule') |
|
1008 | - ->willReturn($defaultEncryptionModule); |
|
1009 | - } |
|
1010 | - $defaultEncryptionModule->method('shouldEncrypt')->willReturn(true); |
|
1011 | - |
|
1012 | - $result = $this->invokePrivate($wrapper, 'shouldEncrypt', [$path]); |
|
1013 | - |
|
1014 | - $this->assertSame($expected, $result); |
|
1015 | - } |
|
1016 | - |
|
1017 | - public static function dataTestShouldEncrypt(): array { |
|
1018 | - return [ |
|
1019 | - [true, false, false, false, false], |
|
1020 | - [true, true, false, false, false], |
|
1021 | - [true, true, true, false, false], |
|
1022 | - [true, true, true, true, true], |
|
1023 | - [true, true, null, false, true], |
|
1024 | - [false, true, true, true, false], |
|
1025 | - ]; |
|
1026 | - } |
|
38 | + /** |
|
39 | + * block size will always be 8192 for a PHP stream |
|
40 | + * @see https://bugs.php.net/bug.php?id=21641 |
|
41 | + */ |
|
42 | + protected int $headerSize = 8192; |
|
43 | + private Temporary $sourceStorage; |
|
44 | + /** @var Encryption&MockObject */ |
|
45 | + protected $instance; |
|
46 | + private \OC\Encryption\Keys\Storage&MockObject $keyStore; |
|
47 | + private Util&MockObject $util; |
|
48 | + private \OC\Encryption\Manager&MockObject $encryptionManager; |
|
49 | + private IEncryptionModule&MockObject $encryptionModule; |
|
50 | + private Cache&MockObject $cache; |
|
51 | + private LoggerInterface&MockObject $logger; |
|
52 | + private File&MockObject $file; |
|
53 | + private MountPoint&MockObject $mount; |
|
54 | + private \OC\Files\Mount\Manager&MockObject $mountManager; |
|
55 | + private \OC\Group\Manager&MockObject $groupManager; |
|
56 | + private IConfig&MockObject $config; |
|
57 | + private ArrayCache&MockObject $arrayCache; |
|
58 | + /** dummy unencrypted size */ |
|
59 | + private int $dummySize = -1; |
|
60 | + |
|
61 | + protected function setUp(): void { |
|
62 | + parent::setUp(); |
|
63 | + |
|
64 | + $mockModule = $this->buildMockModule(); |
|
65 | + $this->encryptionManager = $this->getMockBuilder(\OC\Encryption\Manager::class) |
|
66 | + ->disableOriginalConstructor() |
|
67 | + ->onlyMethods(['getEncryptionModule', 'isEnabled']) |
|
68 | + ->getMock(); |
|
69 | + $this->encryptionManager->expects($this->any()) |
|
70 | + ->method('getEncryptionModule') |
|
71 | + ->willReturn($mockModule); |
|
72 | + |
|
73 | + $this->arrayCache = $this->createMock(ArrayCache::class); |
|
74 | + $this->config = $this->getMockBuilder(IConfig::class) |
|
75 | + ->disableOriginalConstructor() |
|
76 | + ->getMock(); |
|
77 | + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') |
|
78 | + ->disableOriginalConstructor() |
|
79 | + ->getMock(); |
|
80 | + |
|
81 | + $this->util = $this->getMockBuilder(Util::class) |
|
82 | + ->onlyMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension']) |
|
83 | + ->setConstructorArgs([new View(), new Manager( |
|
84 | + $this->config, |
|
85 | + $this->createMock(ICacheFactory::class), |
|
86 | + $this->createMock(IEventDispatcher::class), |
|
87 | + $this->createMock(LoggerInterface::class), |
|
88 | + ), $this->groupManager, $this->config, $this->arrayCache]) |
|
89 | + ->getMock(); |
|
90 | + $this->util->expects($this->any()) |
|
91 | + ->method('getUidAndFilename') |
|
92 | + ->willReturnCallback(function ($path) { |
|
93 | + return ['user1', $path]; |
|
94 | + }); |
|
95 | + $this->util->expects($this->any()) |
|
96 | + ->method('stripPartialFileExtension') |
|
97 | + ->willReturnCallback(function ($path) { |
|
98 | + return $path; |
|
99 | + }); |
|
100 | + |
|
101 | + $this->file = $this->getMockBuilder(File::class) |
|
102 | + ->disableOriginalConstructor() |
|
103 | + ->onlyMethods(['getAccessList']) |
|
104 | + ->getMock(); |
|
105 | + $this->file->expects($this->any())->method('getAccessList')->willReturn([]); |
|
106 | + |
|
107 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
108 | + |
|
109 | + $this->sourceStorage = new Temporary([]); |
|
110 | + |
|
111 | + $this->keyStore = $this->createMock(\OC\Encryption\Keys\Storage::class); |
|
112 | + |
|
113 | + $this->mount = $this->getMockBuilder(MountPoint::class) |
|
114 | + ->disableOriginalConstructor() |
|
115 | + ->onlyMethods(['getOption']) |
|
116 | + ->getMock(); |
|
117 | + $this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) { |
|
118 | + if ($option === 'encrypt' && $default === true) { |
|
119 | + global $mockedMountPointEncryptionEnabled; |
|
120 | + if ($mockedMountPointEncryptionEnabled !== null) { |
|
121 | + return $mockedMountPointEncryptionEnabled; |
|
122 | + } |
|
123 | + } |
|
124 | + return true; |
|
125 | + }); |
|
126 | + |
|
127 | + $this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
128 | + ->disableOriginalConstructor()->getMock(); |
|
129 | + $this->cache->expects($this->any()) |
|
130 | + ->method('get') |
|
131 | + ->willReturnCallback(function ($path) { |
|
132 | + return ['encrypted' => false, 'path' => $path]; |
|
133 | + }); |
|
134 | + |
|
135 | + $this->mountManager = $this->createMock(\OC\Files\Mount\Manager::class); |
|
136 | + $this->mountManager->method('findByStorageId') |
|
137 | + ->willReturn([]); |
|
138 | + |
|
139 | + $this->instance = $this->getMockBuilder(Encryption::class) |
|
140 | + ->setConstructorArgs( |
|
141 | + [ |
|
142 | + [ |
|
143 | + 'storage' => $this->sourceStorage, |
|
144 | + 'root' => 'foo', |
|
145 | + 'mountPoint' => '/', |
|
146 | + 'mount' => $this->mount |
|
147 | + ], |
|
148 | + $this->encryptionManager, |
|
149 | + $this->util, |
|
150 | + $this->logger, |
|
151 | + $this->file, |
|
152 | + null, |
|
153 | + $this->keyStore, |
|
154 | + $this->mountManager, |
|
155 | + $this->arrayCache |
|
156 | + ] |
|
157 | + ) |
|
158 | + ->onlyMethods(['getMetaData', 'getCache', 'getEncryptionModule']) |
|
159 | + ->getMock(); |
|
160 | + |
|
161 | + $this->instance->expects($this->any()) |
|
162 | + ->method('getMetaData') |
|
163 | + ->willReturnCallback(function ($path) { |
|
164 | + return ['encrypted' => true, 'size' => $this->dummySize, 'path' => $path]; |
|
165 | + }); |
|
166 | + |
|
167 | + $this->instance->expects($this->any()) |
|
168 | + ->method('getCache') |
|
169 | + ->willReturn($this->cache); |
|
170 | + |
|
171 | + $this->instance->expects($this->any()) |
|
172 | + ->method('getEncryptionModule') |
|
173 | + ->willReturn($mockModule); |
|
174 | + } |
|
175 | + |
|
176 | + protected function buildMockModule(): IEncryptionModule&MockObject { |
|
177 | + $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') |
|
178 | + ->disableOriginalConstructor() |
|
179 | + ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) |
|
180 | + ->getMock(); |
|
181 | + |
|
182 | + $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); |
|
183 | + $this->encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module'); |
|
184 | + $this->encryptionModule->expects($this->any())->method('begin')->willReturn([]); |
|
185 | + $this->encryptionModule->expects($this->any())->method('end')->willReturn(''); |
|
186 | + $this->encryptionModule->expects($this->any())->method('encrypt')->willReturnArgument(0); |
|
187 | + $this->encryptionModule->expects($this->any())->method('decrypt')->willReturnArgument(0); |
|
188 | + $this->encryptionModule->expects($this->any())->method('update')->willReturn(true); |
|
189 | + $this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true); |
|
190 | + $this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192); |
|
191 | + $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true); |
|
192 | + $this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false); |
|
193 | + return $this->encryptionModule; |
|
194 | + } |
|
195 | + |
|
196 | + /** |
|
197 | + * |
|
198 | + * @param string $path |
|
199 | + * @param array $metaData |
|
200 | + * @param bool $encrypted |
|
201 | + * @param bool $unencryptedSizeSet |
|
202 | + * @param int $storedUnencryptedSize |
|
203 | + * @param array $expected |
|
204 | + */ |
|
205 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetMetaData')] |
|
206 | + public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void { |
|
207 | + $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
208 | + ->disableOriginalConstructor()->getMock(); |
|
209 | + |
|
210 | + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
211 | + ->disableOriginalConstructor()->getMock(); |
|
212 | + $cache->expects($this->any()) |
|
213 | + ->method('get') |
|
214 | + ->willReturnCallback( |
|
215 | + function ($path) use ($encrypted) { |
|
216 | + return new CacheEntry(['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1]); |
|
217 | + } |
|
218 | + ); |
|
219 | + |
|
220 | + $this->instance = $this->getMockBuilder(Encryption::class) |
|
221 | + ->setConstructorArgs( |
|
222 | + [ |
|
223 | + [ |
|
224 | + 'storage' => $sourceStorage, |
|
225 | + 'root' => 'foo', |
|
226 | + 'mountPoint' => '/', |
|
227 | + 'mount' => $this->mount |
|
228 | + ], |
|
229 | + $this->encryptionManager, |
|
230 | + $this->util, |
|
231 | + $this->logger, |
|
232 | + $this->file, |
|
233 | + null, |
|
234 | + $this->keyStore, |
|
235 | + $this->mountManager, |
|
236 | + $this->arrayCache, |
|
237 | + ] |
|
238 | + ) |
|
239 | + ->onlyMethods(['getCache', 'verifyUnencryptedSize']) |
|
240 | + ->getMock(); |
|
241 | + |
|
242 | + if ($unencryptedSizeSet) { |
|
243 | + $this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]); |
|
244 | + } |
|
245 | + |
|
246 | + $fileEntry = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
247 | + ->disableOriginalConstructor()->getMock(); |
|
248 | + $sourceStorage->expects($this->once())->method('getMetaData')->with($path) |
|
249 | + ->willReturn($metaData); |
|
250 | + $sourceStorage->expects($this->any()) |
|
251 | + ->method('getCache') |
|
252 | + ->with($path) |
|
253 | + ->willReturn($fileEntry); |
|
254 | + if ($metaData !== null) { |
|
255 | + $fileEntry->expects($this->any()) |
|
256 | + ->method('get') |
|
257 | + ->with($metaData['fileid']); |
|
258 | + } |
|
259 | + |
|
260 | + $this->instance->expects($this->any())->method('getCache')->willReturn($cache); |
|
261 | + if ($expected !== null) { |
|
262 | + $this->instance->expects($this->any())->method('verifyUnencryptedSize') |
|
263 | + ->with($path, 0)->willReturn($expected['size']); |
|
264 | + } |
|
265 | + |
|
266 | + $result = $this->instance->getMetaData($path); |
|
267 | + if (isset($expected['encrypted'])) { |
|
268 | + $this->assertSame($expected['encrypted'], (bool)$result['encrypted']); |
|
269 | + |
|
270 | + if (isset($expected['encryptedVersion'])) { |
|
271 | + $this->assertSame($expected['encryptedVersion'], $result['encryptedVersion']); |
|
272 | + } |
|
273 | + } |
|
274 | + |
|
275 | + if ($expected !== null) { |
|
276 | + $this->assertSame($expected['size'], $result['size']); |
|
277 | + } else { |
|
278 | + $this->assertSame(null, $result); |
|
279 | + } |
|
280 | + } |
|
281 | + |
|
282 | + public static function dataTestGetMetaData(): array { |
|
283 | + return [ |
|
284 | + ['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]], |
|
285 | + ['/test.txt', null, true, true, 12, null], |
|
286 | + ['/test.txt', ['size' => 42, 'encrypted' => 0, 'fileid' => 1], false, false, 12, ['size' => 42, 'encrypted' => false]], |
|
287 | + ['/test.txt', ['size' => 42, 'encrypted' => false, 'fileid' => 1], true, false, 12, ['size' => 12, 'encrypted' => true]] |
|
288 | + ]; |
|
289 | + } |
|
290 | + |
|
291 | + public function testFilesize(): void { |
|
292 | + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
293 | + ->disableOriginalConstructor()->getMock(); |
|
294 | + $cache->expects($this->any()) |
|
295 | + ->method('get') |
|
296 | + ->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1])); |
|
297 | + |
|
298 | + $this->instance = $this->getMockBuilder(Encryption::class) |
|
299 | + ->setConstructorArgs( |
|
300 | + [ |
|
301 | + [ |
|
302 | + 'storage' => $this->sourceStorage, |
|
303 | + 'root' => 'foo', |
|
304 | + 'mountPoint' => '/', |
|
305 | + 'mount' => $this->mount |
|
306 | + ], |
|
307 | + $this->encryptionManager, |
|
308 | + $this->util, |
|
309 | + $this->logger, |
|
310 | + $this->file, |
|
311 | + null, |
|
312 | + $this->keyStore, |
|
313 | + $this->mountManager, |
|
314 | + $this->arrayCache, |
|
315 | + ] |
|
316 | + ) |
|
317 | + ->onlyMethods(['getCache', 'verifyUnencryptedSize']) |
|
318 | + ->getMock(); |
|
319 | + |
|
320 | + $this->instance->expects($this->any())->method('getCache')->willReturn($cache); |
|
321 | + $this->instance->expects($this->any())->method('verifyUnencryptedSize') |
|
322 | + ->willReturn(42); |
|
323 | + |
|
324 | + |
|
325 | + $this->assertSame(42, |
|
326 | + $this->instance->filesize('/test.txt') |
|
327 | + ); |
|
328 | + } |
|
329 | + |
|
330 | + /** |
|
331 | + * |
|
332 | + * @param int $encryptedSize |
|
333 | + * @param int $unencryptedSize |
|
334 | + * @param bool $failure |
|
335 | + * @param int $expected |
|
336 | + */ |
|
337 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestVerifyUnencryptedSize')] |
|
338 | + public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void { |
|
339 | + $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
340 | + ->disableOriginalConstructor()->getMock(); |
|
341 | + |
|
342 | + $this->instance = $this->getMockBuilder(Encryption::class) |
|
343 | + ->setConstructorArgs( |
|
344 | + [ |
|
345 | + [ |
|
346 | + 'storage' => $sourceStorage, |
|
347 | + 'root' => 'foo', |
|
348 | + 'mountPoint' => '/', |
|
349 | + 'mount' => $this->mount |
|
350 | + ], |
|
351 | + $this->encryptionManager, |
|
352 | + $this->util, |
|
353 | + $this->logger, |
|
354 | + $this->file, |
|
355 | + null, |
|
356 | + $this->keyStore, |
|
357 | + $this->mountManager, |
|
358 | + $this->arrayCache, |
|
359 | + ] |
|
360 | + ) |
|
361 | + ->onlyMethods(['fixUnencryptedSize']) |
|
362 | + ->getMock(); |
|
363 | + |
|
364 | + $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize); |
|
365 | + |
|
366 | + $this->instance->expects($this->any())->method('fixUnencryptedSize') |
|
367 | + ->with('/test.txt', $encryptedSize, $unencryptedSize) |
|
368 | + ->willReturnCallback( |
|
369 | + function () use ($failure, $expected) { |
|
370 | + if ($failure) { |
|
371 | + throw new Exception(); |
|
372 | + } else { |
|
373 | + return $expected; |
|
374 | + } |
|
375 | + } |
|
376 | + ); |
|
377 | + |
|
378 | + $this->assertSame( |
|
379 | + $expected, |
|
380 | + $this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize]) |
|
381 | + ); |
|
382 | + } |
|
383 | + |
|
384 | + public static function dataTestVerifyUnencryptedSize(): array { |
|
385 | + return [ |
|
386 | + [120, 80, false, 80], |
|
387 | + [120, 120, false, 80], |
|
388 | + [120, -1, false, 80], |
|
389 | + [120, -1, true, -1] |
|
390 | + ]; |
|
391 | + } |
|
392 | + |
|
393 | + /** |
|
394 | + * |
|
395 | + * @param string $source |
|
396 | + * @param string $target |
|
397 | + * @param $encryptionEnabled |
|
398 | + * @param boolean $renameKeysReturn |
|
399 | + */ |
|
400 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyAndRename')] |
|
401 | + public function testRename($source, |
|
402 | + $target, |
|
403 | + $encryptionEnabled, |
|
404 | + $renameKeysReturn): void { |
|
405 | + if ($encryptionEnabled) { |
|
406 | + $this->keyStore |
|
407 | + ->expects($this->once()) |
|
408 | + ->method('renameKeys') |
|
409 | + ->willReturn($renameKeysReturn); |
|
410 | + } else { |
|
411 | + $this->keyStore |
|
412 | + ->expects($this->never())->method('renameKeys'); |
|
413 | + } |
|
414 | + $this->util->expects($this->any()) |
|
415 | + ->method('isFile')->willReturn(true); |
|
416 | + $this->encryptionManager->expects($this->once()) |
|
417 | + ->method('isEnabled')->willReturn($encryptionEnabled); |
|
418 | + |
|
419 | + $this->instance->mkdir($source); |
|
420 | + $this->instance->mkdir(dirname($target)); |
|
421 | + $this->instance->rename($source, $target); |
|
422 | + } |
|
423 | + |
|
424 | + public function testCopyEncryption(): void { |
|
425 | + $this->instance->file_put_contents('source.txt', 'bar'); |
|
426 | + $this->instance->copy('source.txt', 'target.txt'); |
|
427 | + $this->assertSame('bar', $this->instance->file_get_contents('target.txt')); |
|
428 | + $targetMeta = $this->instance->getMetaData('target.txt'); |
|
429 | + $sourceMeta = $this->instance->getMetaData('source.txt'); |
|
430 | + $this->assertSame($sourceMeta['encrypted'], $targetMeta['encrypted']); |
|
431 | + $this->assertSame($sourceMeta['size'], $targetMeta['size']); |
|
432 | + } |
|
433 | + |
|
434 | + /** |
|
435 | + * data provider for testCopyTesting() and dataTestCopyAndRename() |
|
436 | + * |
|
437 | + * @return array |
|
438 | + */ |
|
439 | + public static function dataTestCopyAndRename(): array { |
|
440 | + return [ |
|
441 | + ['source', 'target', true, false, false], |
|
442 | + ['source', 'target', true, true, false], |
|
443 | + ['source', '/subFolder/target', true, false, false], |
|
444 | + ['source', '/subFolder/target', true, true, true], |
|
445 | + ['source', '/subFolder/target', false, true, false], |
|
446 | + ]; |
|
447 | + } |
|
448 | + |
|
449 | + public function testIsLocal(): void { |
|
450 | + $this->encryptionManager->expects($this->once()) |
|
451 | + ->method('isEnabled')->willReturn(true); |
|
452 | + $this->assertFalse($this->instance->isLocal()); |
|
453 | + } |
|
454 | + |
|
455 | + /** |
|
456 | + * |
|
457 | + * @param string $path |
|
458 | + * @param boolean $rmdirResult |
|
459 | + * @param boolean $isExcluded |
|
460 | + * @param boolean $encryptionEnabled |
|
461 | + */ |
|
462 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestRmdir')] |
|
463 | + public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void { |
|
464 | + $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
465 | + ->disableOriginalConstructor()->getMock(); |
|
466 | + |
|
467 | + $util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock(); |
|
468 | + |
|
469 | + $sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult); |
|
470 | + $util->expects($this->any())->method('isExcluded')->willReturn($isExcluded); |
|
471 | + $this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled); |
|
472 | + |
|
473 | + $encryptionStorage = new Encryption( |
|
474 | + [ |
|
475 | + 'storage' => $sourceStorage, |
|
476 | + 'root' => 'foo', |
|
477 | + 'mountPoint' => '/mountPoint', |
|
478 | + 'mount' => $this->mount |
|
479 | + ], |
|
480 | + $this->encryptionManager, |
|
481 | + $util, |
|
482 | + $this->logger, |
|
483 | + $this->file, |
|
484 | + null, |
|
485 | + $this->keyStore, |
|
486 | + $this->mountManager, |
|
487 | + $this->arrayCache, |
|
488 | + ); |
|
489 | + |
|
490 | + |
|
491 | + if ($rmdirResult === true && $isExcluded === false && $encryptionEnabled === true) { |
|
492 | + $this->keyStore->expects($this->once())->method('deleteAllFileKeys')->with('/mountPoint' . $path); |
|
493 | + } else { |
|
494 | + $this->keyStore->expects($this->never())->method('deleteAllFileKeys'); |
|
495 | + } |
|
496 | + |
|
497 | + $encryptionStorage->rmdir($path); |
|
498 | + } |
|
499 | + |
|
500 | + public static function dataTestRmdir(): array { |
|
501 | + return [ |
|
502 | + ['/file.txt', true, true, true], |
|
503 | + ['/file.txt', false, true, true], |
|
504 | + ['/file.txt', true, false, true], |
|
505 | + ['/file.txt', false, false, true], |
|
506 | + ['/file.txt', true, true, false], |
|
507 | + ['/file.txt', false, true, false], |
|
508 | + ['/file.txt', true, false, false], |
|
509 | + ['/file.txt', false, false, false], |
|
510 | + ]; |
|
511 | + } |
|
512 | + |
|
513 | + /** |
|
514 | + * |
|
515 | + * @param boolean $excluded |
|
516 | + * @param boolean $expected |
|
517 | + */ |
|
518 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyKeys')] |
|
519 | + public function testCopyKeys($excluded, $expected): void { |
|
520 | + $this->util->expects($this->once()) |
|
521 | + ->method('isExcluded') |
|
522 | + ->willReturn($excluded); |
|
523 | + |
|
524 | + if ($excluded) { |
|
525 | + $this->keyStore->expects($this->never())->method('copyKeys'); |
|
526 | + } else { |
|
527 | + $this->keyStore->expects($this->once())->method('copyKeys')->willReturn(true); |
|
528 | + } |
|
529 | + |
|
530 | + $this->assertSame($expected, |
|
531 | + self::invokePrivate($this->instance, 'copyKeys', ['/source', '/target']) |
|
532 | + ); |
|
533 | + } |
|
534 | + |
|
535 | + public static function dataTestCopyKeys(): array { |
|
536 | + return [ |
|
537 | + [true, false], |
|
538 | + [false, true], |
|
539 | + ]; |
|
540 | + } |
|
541 | + |
|
542 | + /** |
|
543 | + * |
|
544 | + * @param string $path |
|
545 | + * @param bool $strippedPathExists |
|
546 | + * @param string $strippedPath |
|
547 | + */ |
|
548 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeader')] |
|
549 | + public function testGetHeader($path, $strippedPathExists, $strippedPath): void { |
|
550 | + $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') |
|
551 | + ->disableOriginalConstructor()->getMock(); |
|
552 | + |
|
553 | + $util = $this->getMockBuilder('\OC\Encryption\Util') |
|
554 | + ->setConstructorArgs( |
|
555 | + [ |
|
556 | + new View(), |
|
557 | + new Manager( |
|
558 | + $this->config, |
|
559 | + $this->createMock(ICacheFactory::class), |
|
560 | + $this->createMock(IEventDispatcher::class), |
|
561 | + $this->createMock(LoggerInterface::class), |
|
562 | + ), |
|
563 | + $this->groupManager, |
|
564 | + $this->config, |
|
565 | + $this->arrayCache |
|
566 | + ] |
|
567 | + )->getMock(); |
|
568 | + |
|
569 | + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
570 | + ->disableOriginalConstructor()->getMock(); |
|
571 | + $cache->expects($this->any()) |
|
572 | + ->method('get') |
|
573 | + ->willReturnCallback(function ($path) { |
|
574 | + return ['encrypted' => true, 'path' => $path]; |
|
575 | + }); |
|
576 | + |
|
577 | + $instance = $this->getMockBuilder(Encryption::class) |
|
578 | + ->setConstructorArgs( |
|
579 | + [ |
|
580 | + [ |
|
581 | + 'storage' => $sourceStorage, |
|
582 | + 'root' => 'foo', |
|
583 | + 'mountPoint' => '/', |
|
584 | + 'mount' => $this->mount |
|
585 | + ], |
|
586 | + $this->encryptionManager, |
|
587 | + $util, |
|
588 | + $this->logger, |
|
589 | + $this->file, |
|
590 | + null, |
|
591 | + $this->keyStore, |
|
592 | + $this->mountManager, |
|
593 | + $this->arrayCache, |
|
594 | + ] |
|
595 | + ) |
|
596 | + ->onlyMethods(['getCache', 'readFirstBlock']) |
|
597 | + ->getMock(); |
|
598 | + |
|
599 | + $instance->method('getCache')->willReturn($cache); |
|
600 | + |
|
601 | + $util->method('parseRawHeader') |
|
602 | + ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']); |
|
603 | + |
|
604 | + if ($strippedPathExists) { |
|
605 | + $instance->method('readFirstBlock') |
|
606 | + ->with($strippedPath)->willReturn(''); |
|
607 | + } else { |
|
608 | + $instance->method('readFirstBlock') |
|
609 | + ->with($path)->willReturn(''); |
|
610 | + } |
|
611 | + |
|
612 | + $util->expects($this->once())->method('stripPartialFileExtension') |
|
613 | + ->with($path)->willReturn($strippedPath); |
|
614 | + $sourceStorage->expects($this->once()) |
|
615 | + ->method('is_file') |
|
616 | + ->with($strippedPath) |
|
617 | + ->willReturn($strippedPathExists); |
|
618 | + |
|
619 | + $this->invokePrivate($instance, 'getHeader', [$path]); |
|
620 | + } |
|
621 | + |
|
622 | + public static function dataTestGetHeader(): array { |
|
623 | + return [ |
|
624 | + ['/foo/bar.txt', false, '/foo/bar.txt'], |
|
625 | + ['/foo/bar.txt.part', false, '/foo/bar.txt'], |
|
626 | + ['/foo/bar.txt.ocTransferId7437493.part', false, '/foo/bar.txt'], |
|
627 | + ['/foo/bar.txt.part', true, '/foo/bar.txt'], |
|
628 | + ['/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'], |
|
629 | + ]; |
|
630 | + } |
|
631 | + |
|
632 | + /** |
|
633 | + * test if getHeader adds the default module correctly to the header for |
|
634 | + * legacy files |
|
635 | + */ |
|
636 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeaderAddLegacyModule')] |
|
637 | + public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void { |
|
638 | + $sourceStorage = $this->getMockBuilder(\OC\Files\Storage\Storage::class) |
|
639 | + ->disableOriginalConstructor()->getMock(); |
|
640 | + |
|
641 | + $sourceStorage->expects($this->once()) |
|
642 | + ->method('is_file') |
|
643 | + ->with('test.txt') |
|
644 | + ->willReturn($strippedPathExists); |
|
645 | + |
|
646 | + $util = $this->getMockBuilder(Util::class) |
|
647 | + ->onlyMethods(['stripPartialFileExtension', 'parseRawHeader']) |
|
648 | + ->setConstructorArgs([new View(), new Manager( |
|
649 | + $this->config, |
|
650 | + $this->createMock(ICacheFactory::class), |
|
651 | + $this->createMock(IEventDispatcher::class), |
|
652 | + $this->createMock(LoggerInterface::class), |
|
653 | + ), $this->groupManager, $this->config, $this->arrayCache]) |
|
654 | + ->getMock(); |
|
655 | + $util->expects($this->any()) |
|
656 | + ->method('stripPartialFileExtension') |
|
657 | + ->willReturnCallback(function ($path) { |
|
658 | + return $path; |
|
659 | + }); |
|
660 | + |
|
661 | + $cache = $this->createMock(Cache::class); |
|
662 | + $cache->expects($this->any()) |
|
663 | + ->method('get') |
|
664 | + ->willReturnCallback(function ($path) use ($isEncrypted) { |
|
665 | + return ['encrypted' => $isEncrypted, 'path' => $path]; |
|
666 | + }); |
|
667 | + |
|
668 | + $instance = $this->getMockBuilder(Encryption::class) |
|
669 | + ->setConstructorArgs( |
|
670 | + [ |
|
671 | + [ |
|
672 | + 'storage' => $sourceStorage, |
|
673 | + 'root' => 'foo', |
|
674 | + 'mountPoint' => '/', |
|
675 | + 'mount' => $this->mount |
|
676 | + ], |
|
677 | + $this->encryptionManager, |
|
678 | + $util, |
|
679 | + $this->logger, |
|
680 | + $this->file, |
|
681 | + null, |
|
682 | + $this->keyStore, |
|
683 | + $this->mountManager, |
|
684 | + $this->arrayCache, |
|
685 | + ] |
|
686 | + ) |
|
687 | + ->onlyMethods(['readFirstBlock', 'getCache']) |
|
688 | + ->getMock(); |
|
689 | + |
|
690 | + $instance->method('readFirstBlock')->willReturn(''); |
|
691 | + |
|
692 | + $util->method(('parseRawHeader'))->willReturn($header); |
|
693 | + $instance->method('getCache')->willReturn($cache); |
|
694 | + |
|
695 | + $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']); |
|
696 | + $this->assertSameSize($expected, $result); |
|
697 | + foreach ($result as $key => $value) { |
|
698 | + $this->assertArrayHasKey($key, $expected); |
|
699 | + $this->assertSame($expected[$key], $value); |
|
700 | + } |
|
701 | + } |
|
702 | + |
|
703 | + public static function dataTestGetHeaderAddLegacyModule(): array { |
|
704 | + return [ |
|
705 | + [['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], |
|
706 | + [[], true, false, []], |
|
707 | + [[], true, true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], |
|
708 | + [[], false, true, []], |
|
709 | + ]; |
|
710 | + } |
|
711 | + |
|
712 | + public static function dataCopyBetweenStorage(): array { |
|
713 | + return [ |
|
714 | + [true, true, true], |
|
715 | + [true, false, false], |
|
716 | + [false, true, false], |
|
717 | + [false, false, false], |
|
718 | + ]; |
|
719 | + } |
|
720 | + |
|
721 | + public function testCopyBetweenStorageMinimumEncryptedVersion(): void { |
|
722 | + $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); |
|
723 | + |
|
724 | + $sourceInternalPath = $targetInternalPath = 'file.txt'; |
|
725 | + $preserveMtime = $isRename = false; |
|
726 | + |
|
727 | + $storage2->expects($this->any()) |
|
728 | + ->method('fopen') |
|
729 | + ->willReturnCallback(function ($path, $mode) { |
|
730 | + $temp = Server::get(ITempManager::class); |
|
731 | + return fopen($temp->getTemporaryFile(), $mode); |
|
732 | + }); |
|
733 | + $storage2->method('getId') |
|
734 | + ->willReturn('stroage2'); |
|
735 | + $cache = $this->createMock(ICache::class); |
|
736 | + $cache->expects($this->once()) |
|
737 | + ->method('get') |
|
738 | + ->with($sourceInternalPath) |
|
739 | + ->willReturn(['encryptedVersion' => 0]); |
|
740 | + $storage2->expects($this->once()) |
|
741 | + ->method('getCache') |
|
742 | + ->willReturn($cache); |
|
743 | + $this->encryptionManager->expects($this->any()) |
|
744 | + ->method('isEnabled') |
|
745 | + ->willReturn(true); |
|
746 | + global $mockedMountPointEncryptionEnabled; |
|
747 | + $mockedMountPointEncryptionEnabled = true; |
|
748 | + |
|
749 | + $expectedCachePut = [ |
|
750 | + 'encrypted' => true, |
|
751 | + ]; |
|
752 | + $expectedCachePut['encryptedVersion'] = 1; |
|
753 | + |
|
754 | + $this->cache->expects($this->once()) |
|
755 | + ->method('put') |
|
756 | + ->with($sourceInternalPath, $expectedCachePut); |
|
757 | + |
|
758 | + $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]); |
|
759 | + |
|
760 | + $this->assertFalse(false); |
|
761 | + } |
|
762 | + |
|
763 | + /** |
|
764 | + * |
|
765 | + * @param bool $encryptionEnabled |
|
766 | + * @param bool $mountPointEncryptionEnabled |
|
767 | + * @param bool $expectedEncrypted |
|
768 | + */ |
|
769 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataCopyBetweenStorage')] |
|
770 | + public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void { |
|
771 | + $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); |
|
772 | + |
|
773 | + $sourceInternalPath = $targetInternalPath = 'file.txt'; |
|
774 | + $preserveMtime = $isRename = false; |
|
775 | + |
|
776 | + $storage2->expects($this->any()) |
|
777 | + ->method('fopen') |
|
778 | + ->willReturnCallback(function ($path, $mode) { |
|
779 | + $temp = Server::get(ITempManager::class); |
|
780 | + return fopen($temp->getTemporaryFile(), $mode); |
|
781 | + }); |
|
782 | + $storage2->method('getId') |
|
783 | + ->willReturn('stroage2'); |
|
784 | + if ($expectedEncrypted) { |
|
785 | + $cache = $this->createMock(ICache::class); |
|
786 | + $cache->expects($this->once()) |
|
787 | + ->method('get') |
|
788 | + ->with($sourceInternalPath) |
|
789 | + ->willReturn(['encryptedVersion' => 12345]); |
|
790 | + $storage2->expects($this->once()) |
|
791 | + ->method('getCache') |
|
792 | + ->willReturn($cache); |
|
793 | + } |
|
794 | + $this->encryptionManager->expects($this->any()) |
|
795 | + ->method('isEnabled') |
|
796 | + ->willReturn($encryptionEnabled); |
|
797 | + // FIXME can not overwrite the return after definition |
|
798 | + // $this->mount->expects($this->at(0)) |
|
799 | + // ->method('getOption') |
|
800 | + // ->with('encrypt', true) |
|
801 | + // ->willReturn($mountPointEncryptionEnabled); |
|
802 | + global $mockedMountPointEncryptionEnabled; |
|
803 | + $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled; |
|
804 | + |
|
805 | + $expectedCachePut = [ |
|
806 | + 'encrypted' => $expectedEncrypted, |
|
807 | + ]; |
|
808 | + if ($expectedEncrypted === true) { |
|
809 | + $expectedCachePut['encryptedVersion'] = 1; |
|
810 | + } |
|
811 | + |
|
812 | + $this->arrayCache->expects($this->never())->method('set'); |
|
813 | + |
|
814 | + $this->cache->expects($this->once()) |
|
815 | + ->method('put') |
|
816 | + ->with($sourceInternalPath, $expectedCachePut); |
|
817 | + |
|
818 | + $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]); |
|
819 | + |
|
820 | + $this->assertFalse(false); |
|
821 | + } |
|
822 | + |
|
823 | + /** |
|
824 | + * |
|
825 | + * @param string $sourceInternalPath |
|
826 | + * @param string $targetInternalPath |
|
827 | + * @param bool $copyResult |
|
828 | + * @param bool $encrypted |
|
829 | + */ |
|
830 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyBetweenStorageVersions')] |
|
831 | + public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void { |
|
832 | + $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class); |
|
833 | + |
|
834 | + $targetStorage = $this->createMock(\OC\Files\Storage\Storage::class); |
|
835 | + |
|
836 | + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') |
|
837 | + ->disableOriginalConstructor()->getMock(); |
|
838 | + |
|
839 | + $mountPoint = '/mountPoint'; |
|
840 | + |
|
841 | + /** @var Encryption |MockObject $instance */ |
|
842 | + $instance = $this->getMockBuilder(Encryption::class) |
|
843 | + ->setConstructorArgs( |
|
844 | + [ |
|
845 | + [ |
|
846 | + 'storage' => $targetStorage, |
|
847 | + 'root' => 'foo', |
|
848 | + 'mountPoint' => $mountPoint, |
|
849 | + 'mount' => $this->mount |
|
850 | + ], |
|
851 | + $this->encryptionManager, |
|
852 | + $this->util, |
|
853 | + $this->logger, |
|
854 | + $this->file, |
|
855 | + null, |
|
856 | + $this->keyStore, |
|
857 | + $this->mountManager, |
|
858 | + $this->arrayCache |
|
859 | + ] |
|
860 | + ) |
|
861 | + ->onlyMethods(['updateUnencryptedSize', 'getCache']) |
|
862 | + ->getMock(); |
|
863 | + |
|
864 | + $targetStorage->expects($this->once())->method('copyFromStorage') |
|
865 | + ->with($sourceStorage, $sourceInternalPath, $targetInternalPath) |
|
866 | + ->willReturn($copyResult); |
|
867 | + |
|
868 | + $instance->expects($this->any())->method('getCache') |
|
869 | + ->willReturn($cache); |
|
870 | + |
|
871 | + $this->arrayCache->expects($this->once())->method('set') |
|
872 | + ->with('encryption_copy_version_' . $sourceInternalPath, true); |
|
873 | + |
|
874 | + if ($copyResult) { |
|
875 | + $cache->expects($this->once())->method('get') |
|
876 | + ->with($sourceInternalPath) |
|
877 | + ->willReturn(new CacheEntry(['encrypted' => $encrypted, 'size' => 42])); |
|
878 | + if ($encrypted) { |
|
879 | + $instance->expects($this->once())->method('updateUnencryptedSize') |
|
880 | + ->with($mountPoint . $targetInternalPath, 42); |
|
881 | + } else { |
|
882 | + $instance->expects($this->never())->method('updateUnencryptedSize'); |
|
883 | + } |
|
884 | + } else { |
|
885 | + $instance->expects($this->never())->method('updateUnencryptedSize'); |
|
886 | + } |
|
887 | + |
|
888 | + $result = $this->invokePrivate( |
|
889 | + $instance, |
|
890 | + 'copyBetweenStorage', |
|
891 | + [ |
|
892 | + $sourceStorage, |
|
893 | + $sourceInternalPath, |
|
894 | + $targetInternalPath, |
|
895 | + false, |
|
896 | + false |
|
897 | + ] |
|
898 | + ); |
|
899 | + |
|
900 | + $this->assertSame($copyResult, $result); |
|
901 | + } |
|
902 | + |
|
903 | + public static function dataTestCopyBetweenStorageVersions(): array { |
|
904 | + return [ |
|
905 | + ['/files/foo.txt', '/files_versions/foo.txt.768743', true, true], |
|
906 | + ['/files/foo.txt', '/files_versions/foo.txt.768743', true, false], |
|
907 | + ['/files/foo.txt', '/files_versions/foo.txt.768743', false, true], |
|
908 | + ['/files/foo.txt', '/files_versions/foo.txt.768743', false, false], |
|
909 | + ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, true], |
|
910 | + ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, false], |
|
911 | + ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, true], |
|
912 | + ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, false], |
|
913 | + |
|
914 | + ]; |
|
915 | + } |
|
916 | + |
|
917 | + /** |
|
918 | + * @param string $path |
|
919 | + * @param bool $expected |
|
920 | + */ |
|
921 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsVersion')] |
|
922 | + public function testIsVersion($path, $expected): void { |
|
923 | + $this->assertSame($expected, |
|
924 | + $this->invokePrivate($this->instance, 'isVersion', [$path]) |
|
925 | + ); |
|
926 | + } |
|
927 | + |
|
928 | + public static function dataTestIsVersion(): array { |
|
929 | + return [ |
|
930 | + ['files_versions/foo', true], |
|
931 | + ['/files_versions/foo', true], |
|
932 | + ['//files_versions/foo', true], |
|
933 | + ['files/versions/foo', false], |
|
934 | + ['files/files_versions/foo', false], |
|
935 | + ['files_versions_test/foo', false], |
|
936 | + ]; |
|
937 | + } |
|
938 | + |
|
939 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')] |
|
940 | + public function testShouldEncrypt( |
|
941 | + bool $encryptionEnabled, |
|
942 | + bool $encryptMountPoint, |
|
943 | + ?bool $encryptionModule, |
|
944 | + bool $encryptionModuleShouldEncrypt, |
|
945 | + bool $expected, |
|
946 | + ): void { |
|
947 | + $encryptionManager = $this->createMock(\OC\Encryption\Manager::class); |
|
948 | + $util = $this->createMock(Util::class); |
|
949 | + $fileHelper = $this->createMock(IFile::class); |
|
950 | + $keyStorage = $this->createMock(IStorage::class); |
|
951 | + $mountManager = $this->createMock(\OC\Files\Mount\Manager::class); |
|
952 | + $mount = $this->createMock(IMountPoint::class); |
|
953 | + $arrayCache = $this->createMock(ArrayCache::class); |
|
954 | + $path = '/welcome.txt'; |
|
955 | + $fullPath = 'admin/files/welcome.txt'; |
|
956 | + $defaultEncryptionModule = $this->createMock(IEncryptionModule::class); |
|
957 | + |
|
958 | + $wrapper = $this->getMockBuilder(Encryption::class) |
|
959 | + ->setConstructorArgs( |
|
960 | + [ |
|
961 | + ['mountPoint' => '', 'mount' => $mount, 'storage' => ''], |
|
962 | + $encryptionManager, |
|
963 | + $util, |
|
964 | + $this->logger, |
|
965 | + $fileHelper, |
|
966 | + null, |
|
967 | + $keyStorage, |
|
968 | + $mountManager, |
|
969 | + $arrayCache |
|
970 | + ] |
|
971 | + ) |
|
972 | + ->onlyMethods(['getFullPath', 'getEncryptionModule']) |
|
973 | + ->getMock(); |
|
974 | + |
|
975 | + $encryptionManager->method('isEnabled')->willReturn($encryptionEnabled); |
|
976 | + |
|
977 | + if ($encryptionModule === true) { |
|
978 | + /** @var IEncryptionModule|MockObject $encryptionModule */ |
|
979 | + $encryptionModule = $this->createMock(IEncryptionModule::class); |
|
980 | + } |
|
981 | + |
|
982 | + $wrapper->method('getFullPath')->with($path)->willReturn($fullPath); |
|
983 | + $wrapper->expects(($encryptionEnabled && $encryptMountPoint) ? $this->once() : $this->never()) |
|
984 | + ->method('getEncryptionModule') |
|
985 | + ->with($fullPath) |
|
986 | + ->willReturnCallback( |
|
987 | + function () use ($encryptionModule) { |
|
988 | + if ($encryptionModule === false) { |
|
989 | + throw new ModuleDoesNotExistsException(); |
|
990 | + } |
|
991 | + return $encryptionModule; |
|
992 | + } |
|
993 | + ); |
|
994 | + $mount->expects($encryptionEnabled ? $this->once() : $this->never()) |
|
995 | + ->method('getOption')->with('encrypt', true) |
|
996 | + ->willReturn($encryptMountPoint); |
|
997 | + |
|
998 | + if ($encryptionModule !== null && $encryptionModule !== false) { |
|
999 | + $encryptionModule |
|
1000 | + ->method('shouldEncrypt') |
|
1001 | + ->with($fullPath) |
|
1002 | + ->willReturn($encryptionModuleShouldEncrypt); |
|
1003 | + } |
|
1004 | + |
|
1005 | + if ($encryptionModule === null) { |
|
1006 | + $encryptionManager->expects($this->once()) |
|
1007 | + ->method('getEncryptionModule') |
|
1008 | + ->willReturn($defaultEncryptionModule); |
|
1009 | + } |
|
1010 | + $defaultEncryptionModule->method('shouldEncrypt')->willReturn(true); |
|
1011 | + |
|
1012 | + $result = $this->invokePrivate($wrapper, 'shouldEncrypt', [$path]); |
|
1013 | + |
|
1014 | + $this->assertSame($expected, $result); |
|
1015 | + } |
|
1016 | + |
|
1017 | + public static function dataTestShouldEncrypt(): array { |
|
1018 | + return [ |
|
1019 | + [true, false, false, false, false], |
|
1020 | + [true, true, false, false, false], |
|
1021 | + [true, true, true, false, false], |
|
1022 | + [true, true, true, true, true], |
|
1023 | + [true, true, null, false, true], |
|
1024 | + [false, true, true, true, false], |
|
1025 | + ]; |
|
1026 | + } |
|
1027 | 1027 | } |
@@ -10,145 +10,145 @@ |
||
10 | 10 | require __DIR__ . '/../../vendor/autoload.php'; |
11 | 11 | |
12 | 12 | trait CommandLine { |
13 | - /** @var int return code of last command */ |
|
14 | - private $lastCode; |
|
15 | - /** @var string stdout of last command */ |
|
16 | - private $lastStdOut; |
|
17 | - /** @var string stderr of last command */ |
|
18 | - private $lastStdErr; |
|
19 | - |
|
20 | - /** @var string */ |
|
21 | - protected $ocPath = '../..'; |
|
22 | - |
|
23 | - /** |
|
24 | - * Invokes an OCC command |
|
25 | - * |
|
26 | - * @param []string $args OCC command, the part behind "occ". For example: "files:transfer-ownership" |
|
27 | - * @return int exit code |
|
28 | - */ |
|
29 | - public function runOcc($args = [], string $inputString = '') { |
|
30 | - $args = array_map(function ($arg) { |
|
31 | - return escapeshellarg($arg); |
|
32 | - }, $args); |
|
33 | - $args[] = '--no-ansi'; |
|
34 | - $args = implode(' ', $args); |
|
35 | - |
|
36 | - $descriptor = [ |
|
37 | - 0 => ['pipe', 'r'], |
|
38 | - 1 => ['pipe', 'w'], |
|
39 | - 2 => ['pipe', 'w'], |
|
40 | - ]; |
|
41 | - $process = proc_open('php console.php ' . $args, $descriptor, $pipes, $this->ocPath); |
|
42 | - if ($inputString !== '') { |
|
43 | - fwrite($pipes[0], $inputString . "\n"); |
|
44 | - fclose($pipes[0]); |
|
45 | - } |
|
46 | - $this->lastStdOut = stream_get_contents($pipes[1]); |
|
47 | - $this->lastStdErr = stream_get_contents($pipes[2]); |
|
48 | - $this->lastCode = proc_close($process); |
|
49 | - |
|
50 | - // Clean opcode cache |
|
51 | - $client = new GuzzleHttp\Client(); |
|
52 | - $client->request('GET', 'http://localhost:8080/apps/testing/clean_opcode_cache.php'); |
|
53 | - |
|
54 | - return $this->lastCode; |
|
55 | - } |
|
56 | - |
|
57 | - /** |
|
58 | - * @Given /^invoking occ with "([^"]*)"$/ |
|
59 | - */ |
|
60 | - public function invokingTheCommand($cmd) { |
|
61 | - $args = explode(' ', $cmd); |
|
62 | - $this->runOcc($args); |
|
63 | - } |
|
64 | - |
|
65 | - /** |
|
66 | - * @Given /^invoking occ with "([^"]*)" with input "([^"]+)"$/ |
|
67 | - */ |
|
68 | - public function invokingTheCommandWith($cmd, $inputString) { |
|
69 | - $args = explode(' ', $cmd); |
|
70 | - $this->runOcc($args, $inputString); |
|
71 | - } |
|
72 | - |
|
73 | - /** |
|
74 | - * Find exception texts in stderr |
|
75 | - */ |
|
76 | - public function findExceptions() { |
|
77 | - $exceptions = []; |
|
78 | - $captureNext = false; |
|
79 | - // the exception text usually appears after an "[Exception"] row |
|
80 | - foreach (explode("\n", $this->lastStdErr) as $line) { |
|
81 | - if (preg_match('/\[Exception\]/', $line)) { |
|
82 | - $captureNext = true; |
|
83 | - continue; |
|
84 | - } |
|
85 | - if ($captureNext) { |
|
86 | - $exceptions[] = trim($line); |
|
87 | - $captureNext = false; |
|
88 | - } |
|
89 | - } |
|
90 | - |
|
91 | - return $exceptions; |
|
92 | - } |
|
93 | - |
|
94 | - /** |
|
95 | - * @Then /^the command was successful$/ |
|
96 | - */ |
|
97 | - public function theCommandWasSuccessful() { |
|
98 | - $exceptions = $this->findExceptions(); |
|
99 | - if ($this->lastCode !== 0) { |
|
100 | - $msg = 'The command was not successful, exit code was ' . $this->lastCode . '.'; |
|
101 | - if (!empty($exceptions)) { |
|
102 | - $msg .= ' Exceptions: ' . implode(', ', $exceptions); |
|
103 | - } |
|
104 | - throw new \Exception($msg); |
|
105 | - } elseif (!empty($exceptions)) { |
|
106 | - $msg = 'The command was successful but triggered exceptions: ' . implode(', ', $exceptions); |
|
107 | - throw new \Exception($msg); |
|
108 | - } |
|
109 | - } |
|
110 | - |
|
111 | - /** |
|
112 | - * @Then /^the command failed with exit code ([0-9]+)$/ |
|
113 | - */ |
|
114 | - public function theCommandFailedWithExitCode($exitCode) { |
|
115 | - if ($this->lastCode !== (int)$exitCode) { |
|
116 | - throw new \Exception('The command was expected to fail with exit code ' . $exitCode . ' but got ' . $this->lastCode); |
|
117 | - } |
|
118 | - } |
|
119 | - |
|
120 | - /** |
|
121 | - * @Then /^the command failed with exception text "([^"]*)"$/ |
|
122 | - */ |
|
123 | - public function theCommandFailedWithException($exceptionText) { |
|
124 | - $exceptions = $this->findExceptions(); |
|
125 | - if (empty($exceptions)) { |
|
126 | - throw new \Exception('The command did not throw any exceptions'); |
|
127 | - } |
|
128 | - |
|
129 | - if (!in_array($exceptionText, $exceptions)) { |
|
130 | - throw new \Exception('The command did not throw any exception with the text "' . $exceptionText . '"'); |
|
131 | - } |
|
132 | - } |
|
133 | - |
|
134 | - /** |
|
135 | - * @Then /^the command output contains the text "([^"]*)"$/ |
|
136 | - */ |
|
137 | - public function theCommandOutputContainsTheText($text) { |
|
138 | - Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout'); |
|
139 | - } |
|
140 | - |
|
141 | - /** |
|
142 | - * @Then /^the command output does not contain the text "([^"]*)"$/ |
|
143 | - */ |
|
144 | - public function theCommandOutputDoesNotContainTheText($text) { |
|
145 | - Assert::assertStringNotContainsString($text, $this->lastStdOut, 'The command did output the not expected text on stdout'); |
|
146 | - } |
|
147 | - |
|
148 | - /** |
|
149 | - * @Then /^the command error output contains the text "([^"]*)"$/ |
|
150 | - */ |
|
151 | - public function theCommandErrorOutputContainsTheText($text) { |
|
152 | - Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stderr'); |
|
153 | - } |
|
13 | + /** @var int return code of last command */ |
|
14 | + private $lastCode; |
|
15 | + /** @var string stdout of last command */ |
|
16 | + private $lastStdOut; |
|
17 | + /** @var string stderr of last command */ |
|
18 | + private $lastStdErr; |
|
19 | + |
|
20 | + /** @var string */ |
|
21 | + protected $ocPath = '../..'; |
|
22 | + |
|
23 | + /** |
|
24 | + * Invokes an OCC command |
|
25 | + * |
|
26 | + * @param []string $args OCC command, the part behind "occ". For example: "files:transfer-ownership" |
|
27 | + * @return int exit code |
|
28 | + */ |
|
29 | + public function runOcc($args = [], string $inputString = '') { |
|
30 | + $args = array_map(function ($arg) { |
|
31 | + return escapeshellarg($arg); |
|
32 | + }, $args); |
|
33 | + $args[] = '--no-ansi'; |
|
34 | + $args = implode(' ', $args); |
|
35 | + |
|
36 | + $descriptor = [ |
|
37 | + 0 => ['pipe', 'r'], |
|
38 | + 1 => ['pipe', 'w'], |
|
39 | + 2 => ['pipe', 'w'], |
|
40 | + ]; |
|
41 | + $process = proc_open('php console.php ' . $args, $descriptor, $pipes, $this->ocPath); |
|
42 | + if ($inputString !== '') { |
|
43 | + fwrite($pipes[0], $inputString . "\n"); |
|
44 | + fclose($pipes[0]); |
|
45 | + } |
|
46 | + $this->lastStdOut = stream_get_contents($pipes[1]); |
|
47 | + $this->lastStdErr = stream_get_contents($pipes[2]); |
|
48 | + $this->lastCode = proc_close($process); |
|
49 | + |
|
50 | + // Clean opcode cache |
|
51 | + $client = new GuzzleHttp\Client(); |
|
52 | + $client->request('GET', 'http://localhost:8080/apps/testing/clean_opcode_cache.php'); |
|
53 | + |
|
54 | + return $this->lastCode; |
|
55 | + } |
|
56 | + |
|
57 | + /** |
|
58 | + * @Given /^invoking occ with "([^"]*)"$/ |
|
59 | + */ |
|
60 | + public function invokingTheCommand($cmd) { |
|
61 | + $args = explode(' ', $cmd); |
|
62 | + $this->runOcc($args); |
|
63 | + } |
|
64 | + |
|
65 | + /** |
|
66 | + * @Given /^invoking occ with "([^"]*)" with input "([^"]+)"$/ |
|
67 | + */ |
|
68 | + public function invokingTheCommandWith($cmd, $inputString) { |
|
69 | + $args = explode(' ', $cmd); |
|
70 | + $this->runOcc($args, $inputString); |
|
71 | + } |
|
72 | + |
|
73 | + /** |
|
74 | + * Find exception texts in stderr |
|
75 | + */ |
|
76 | + public function findExceptions() { |
|
77 | + $exceptions = []; |
|
78 | + $captureNext = false; |
|
79 | + // the exception text usually appears after an "[Exception"] row |
|
80 | + foreach (explode("\n", $this->lastStdErr) as $line) { |
|
81 | + if (preg_match('/\[Exception\]/', $line)) { |
|
82 | + $captureNext = true; |
|
83 | + continue; |
|
84 | + } |
|
85 | + if ($captureNext) { |
|
86 | + $exceptions[] = trim($line); |
|
87 | + $captureNext = false; |
|
88 | + } |
|
89 | + } |
|
90 | + |
|
91 | + return $exceptions; |
|
92 | + } |
|
93 | + |
|
94 | + /** |
|
95 | + * @Then /^the command was successful$/ |
|
96 | + */ |
|
97 | + public function theCommandWasSuccessful() { |
|
98 | + $exceptions = $this->findExceptions(); |
|
99 | + if ($this->lastCode !== 0) { |
|
100 | + $msg = 'The command was not successful, exit code was ' . $this->lastCode . '.'; |
|
101 | + if (!empty($exceptions)) { |
|
102 | + $msg .= ' Exceptions: ' . implode(', ', $exceptions); |
|
103 | + } |
|
104 | + throw new \Exception($msg); |
|
105 | + } elseif (!empty($exceptions)) { |
|
106 | + $msg = 'The command was successful but triggered exceptions: ' . implode(', ', $exceptions); |
|
107 | + throw new \Exception($msg); |
|
108 | + } |
|
109 | + } |
|
110 | + |
|
111 | + /** |
|
112 | + * @Then /^the command failed with exit code ([0-9]+)$/ |
|
113 | + */ |
|
114 | + public function theCommandFailedWithExitCode($exitCode) { |
|
115 | + if ($this->lastCode !== (int)$exitCode) { |
|
116 | + throw new \Exception('The command was expected to fail with exit code ' . $exitCode . ' but got ' . $this->lastCode); |
|
117 | + } |
|
118 | + } |
|
119 | + |
|
120 | + /** |
|
121 | + * @Then /^the command failed with exception text "([^"]*)"$/ |
|
122 | + */ |
|
123 | + public function theCommandFailedWithException($exceptionText) { |
|
124 | + $exceptions = $this->findExceptions(); |
|
125 | + if (empty($exceptions)) { |
|
126 | + throw new \Exception('The command did not throw any exceptions'); |
|
127 | + } |
|
128 | + |
|
129 | + if (!in_array($exceptionText, $exceptions)) { |
|
130 | + throw new \Exception('The command did not throw any exception with the text "' . $exceptionText . '"'); |
|
131 | + } |
|
132 | + } |
|
133 | + |
|
134 | + /** |
|
135 | + * @Then /^the command output contains the text "([^"]*)"$/ |
|
136 | + */ |
|
137 | + public function theCommandOutputContainsTheText($text) { |
|
138 | + Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout'); |
|
139 | + } |
|
140 | + |
|
141 | + /** |
|
142 | + * @Then /^the command output does not contain the text "([^"]*)"$/ |
|
143 | + */ |
|
144 | + public function theCommandOutputDoesNotContainTheText($text) { |
|
145 | + Assert::assertStringNotContainsString($text, $this->lastStdOut, 'The command did output the not expected text on stdout'); |
|
146 | + } |
|
147 | + |
|
148 | + /** |
|
149 | + * @Then /^the command error output contains the text "([^"]*)"$/ |
|
150 | + */ |
|
151 | + public function theCommandErrorOutputContainsTheText($text) { |
|
152 | + Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stderr'); |
|
153 | + } |
|
154 | 154 | } |
@@ -7,7 +7,7 @@ discard block |
||
7 | 7 | */ |
8 | 8 | use PHPUnit\Framework\Assert; |
9 | 9 | |
10 | -require __DIR__ . '/../../vendor/autoload.php'; |
|
10 | +require __DIR__.'/../../vendor/autoload.php'; |
|
11 | 11 | |
12 | 12 | trait CommandLine { |
13 | 13 | /** @var int return code of last command */ |
@@ -27,7 +27,7 @@ discard block |
||
27 | 27 | * @return int exit code |
28 | 28 | */ |
29 | 29 | public function runOcc($args = [], string $inputString = '') { |
30 | - $args = array_map(function ($arg) { |
|
30 | + $args = array_map(function($arg) { |
|
31 | 31 | return escapeshellarg($arg); |
32 | 32 | }, $args); |
33 | 33 | $args[] = '--no-ansi'; |
@@ -38,9 +38,9 @@ discard block |
||
38 | 38 | 1 => ['pipe', 'w'], |
39 | 39 | 2 => ['pipe', 'w'], |
40 | 40 | ]; |
41 | - $process = proc_open('php console.php ' . $args, $descriptor, $pipes, $this->ocPath); |
|
41 | + $process = proc_open('php console.php '.$args, $descriptor, $pipes, $this->ocPath); |
|
42 | 42 | if ($inputString !== '') { |
43 | - fwrite($pipes[0], $inputString . "\n"); |
|
43 | + fwrite($pipes[0], $inputString."\n"); |
|
44 | 44 | fclose($pipes[0]); |
45 | 45 | } |
46 | 46 | $this->lastStdOut = stream_get_contents($pipes[1]); |
@@ -97,13 +97,13 @@ discard block |
||
97 | 97 | public function theCommandWasSuccessful() { |
98 | 98 | $exceptions = $this->findExceptions(); |
99 | 99 | if ($this->lastCode !== 0) { |
100 | - $msg = 'The command was not successful, exit code was ' . $this->lastCode . '.'; |
|
100 | + $msg = 'The command was not successful, exit code was '.$this->lastCode.'.'; |
|
101 | 101 | if (!empty($exceptions)) { |
102 | - $msg .= ' Exceptions: ' . implode(', ', $exceptions); |
|
102 | + $msg .= ' Exceptions: '.implode(', ', $exceptions); |
|
103 | 103 | } |
104 | 104 | throw new \Exception($msg); |
105 | 105 | } elseif (!empty($exceptions)) { |
106 | - $msg = 'The command was successful but triggered exceptions: ' . implode(', ', $exceptions); |
|
106 | + $msg = 'The command was successful but triggered exceptions: '.implode(', ', $exceptions); |
|
107 | 107 | throw new \Exception($msg); |
108 | 108 | } |
109 | 109 | } |
@@ -112,8 +112,8 @@ discard block |
||
112 | 112 | * @Then /^the command failed with exit code ([0-9]+)$/ |
113 | 113 | */ |
114 | 114 | public function theCommandFailedWithExitCode($exitCode) { |
115 | - if ($this->lastCode !== (int)$exitCode) { |
|
116 | - throw new \Exception('The command was expected to fail with exit code ' . $exitCode . ' but got ' . $this->lastCode); |
|
115 | + if ($this->lastCode !== (int) $exitCode) { |
|
116 | + throw new \Exception('The command was expected to fail with exit code '.$exitCode.' but got '.$this->lastCode); |
|
117 | 117 | } |
118 | 118 | } |
119 | 119 | |
@@ -127,7 +127,7 @@ discard block |
||
127 | 127 | } |
128 | 128 | |
129 | 129 | if (!in_array($exceptionText, $exceptions)) { |
130 | - throw new \Exception('The command did not throw any exception with the text "' . $exceptionText . '"'); |
|
130 | + throw new \Exception('The command did not throw any exception with the text "'.$exceptionText.'"'); |
|
131 | 131 | } |
132 | 132 | } |
133 | 133 |