EncryptionTest.php 31 KB

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