You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1070 lines
33 KiB

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