EncryptionTest.php 31 KB

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