StorageTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @copyright (C) 2015 ownCloud, Inc.
  6. *
  7. * @author Bjoern Schiessle <schiessle@owncloud.com>
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. namespace Test\Encryption\Keys;
  23. use OC\Encryption\Keys\Storage;
  24. use Test\TestCase;
  25. class StorageTest extends TestCase {
  26. /** @var Storage */
  27. protected $storage;
  28. /** @var \PHPUnit_Framework_MockObject_MockObject */
  29. protected $util;
  30. /** @var \PHPUnit_Framework_MockObject_MockObject */
  31. protected $view;
  32. /** @var \PHPUnit_Framework_MockObject_MockObject */
  33. protected $config;
  34. public function setUp() {
  35. parent::setUp();
  36. $this->util = $this->getMockBuilder('OC\Encryption\Util')
  37. ->disableOriginalConstructor()
  38. ->getMock();
  39. $this->view = $this->getMockBuilder('OC\Files\View')
  40. ->disableOriginalConstructor()
  41. ->getMock();
  42. $this->config = $this->getMockBuilder('OCP\IConfig')
  43. ->disableOriginalConstructor()
  44. ->getMock();
  45. $this->storage = new Storage($this->view, $this->util);
  46. }
  47. public function testSetFileKey() {
  48. $this->util->expects($this->any())
  49. ->method('getUidAndFilename')
  50. ->willReturn(array('user1', '/files/foo.txt'));
  51. $this->util->expects($this->any())
  52. ->method('stripPartialFileExtension')
  53. ->willReturnArgument(0);
  54. $this->util->expects($this->any())
  55. ->method('isSystemWideMountPoint')
  56. ->willReturn(false);
  57. $this->view->expects($this->once())
  58. ->method('file_put_contents')
  59. ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'),
  60. $this->equalTo('key'))
  61. ->willReturn(strlen('key'));
  62. $this->assertTrue(
  63. $this->storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key', 'encModule')
  64. );
  65. }
  66. public function dataTestGetFileKey() {
  67. return [
  68. ['/files/foo.txt', '/files/foo.txt', true, 'key'],
  69. ['/files/foo.txt.ocTransferId2111130212.part', '/files/foo.txt', true, 'key'],
  70. ['/files/foo.txt.ocTransferId2111130212.part', '/files/foo.txt', false, 'key2'],
  71. ];
  72. }
  73. /**
  74. * @dataProvider dataTestGetFileKey
  75. *
  76. * @param string $path
  77. * @param string $strippedPartialName
  78. * @param bool $originalKeyExists
  79. * @param string $expectedKeyContent
  80. */
  81. public function testGetFileKey($path, $strippedPartialName, $originalKeyExists, $expectedKeyContent) {
  82. $this->util->expects($this->any())
  83. ->method('getUidAndFilename')
  84. ->willReturnMap([
  85. ['user1/files/foo.txt', ['user1', '/files/foo.txt']],
  86. ['user1/files/foo.txt.ocTransferId2111130212.part', ['user1', '/files/foo.txt.ocTransferId2111130212.part']],
  87. ]);
  88. // we need to strip away the part file extension in order to reuse a
  89. // existing key if it exists, otherwise versions will break
  90. $this->util->expects($this->once())
  91. ->method('stripPartialFileExtension')
  92. ->willReturn('user1' . $strippedPartialName);
  93. $this->util->expects($this->any())
  94. ->method('isSystemWideMountPoint')
  95. ->willReturn(false);
  96. $this->view->expects($this->at(0))
  97. ->method('file_exists')
  98. ->with($this->equalTo('/user1/files_encryption/keys' . $strippedPartialName . '/encModule/fileKey'))
  99. ->willReturn($originalKeyExists);
  100. if (!$originalKeyExists) {
  101. $this->view->expects($this->at(1))
  102. ->method('file_exists')
  103. ->with($this->equalTo('/user1/files_encryption/keys' . $path . '/encModule/fileKey'))
  104. ->willReturn(true);
  105. $this->view->expects($this->once())
  106. ->method('file_get_contents')
  107. ->with($this->equalTo('/user1/files_encryption/keys' . $path . '/encModule/fileKey'))
  108. ->willReturn('key2');
  109. } else {
  110. $this->view->expects($this->once())
  111. ->method('file_get_contents')
  112. ->with($this->equalTo('/user1/files_encryption/keys' . $strippedPartialName . '/encModule/fileKey'))
  113. ->willReturn('key');
  114. }
  115. $this->assertSame($expectedKeyContent,
  116. $this->storage->getFileKey('user1' . $path, 'fileKey', 'encModule')
  117. );
  118. }
  119. public function testSetFileKeySystemWide() {
  120. $this->util->expects($this->any())
  121. ->method('getUidAndFilename')
  122. ->willReturn(array('user1', '/files/foo.txt'));
  123. $this->util->expects($this->any())
  124. ->method('isSystemWideMountPoint')
  125. ->willReturn(true);
  126. $this->util->expects($this->any())
  127. ->method('stripPartialFileExtension')
  128. ->willReturnArgument(0);
  129. $this->view->expects($this->once())
  130. ->method('file_put_contents')
  131. ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'),
  132. $this->equalTo('key'))
  133. ->willReturn(strlen('key'));
  134. $this->assertTrue(
  135. $this->storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key', 'encModule')
  136. );
  137. }
  138. public function testGetFileKeySystemWide() {
  139. $this->util->expects($this->any())
  140. ->method('getUidAndFilename')
  141. ->willReturn(array('user1', '/files/foo.txt'));
  142. $this->util->expects($this->any())
  143. ->method('stripPartialFileExtension')
  144. ->willReturnArgument(0);
  145. $this->util->expects($this->any())
  146. ->method('isSystemWideMountPoint')
  147. ->willReturn(true);
  148. $this->view->expects($this->once())
  149. ->method('file_get_contents')
  150. ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  151. ->willReturn('key');
  152. $this->view->expects($this->once())
  153. ->method('file_exists')
  154. ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  155. ->willReturn(true);
  156. $this->assertSame('key',
  157. $this->storage->getFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
  158. );
  159. }
  160. public function testSetSystemUserKey() {
  161. $this->view->expects($this->once())
  162. ->method('file_put_contents')
  163. ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'),
  164. $this->equalTo('key'))
  165. ->willReturn(strlen('key'));
  166. $this->assertTrue(
  167. $this->storage->setSystemUserKey('shareKey_56884', 'key', 'encModule')
  168. );
  169. }
  170. public function testSetUserKey() {
  171. $this->view->expects($this->once())
  172. ->method('file_put_contents')
  173. ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'),
  174. $this->equalTo('key'))
  175. ->willReturn(strlen('key'));
  176. $this->assertTrue(
  177. $this->storage->setUserKey('user1', 'publicKey', 'key', 'encModule')
  178. );
  179. }
  180. public function testGetSystemUserKey() {
  181. $this->view->expects($this->once())
  182. ->method('file_get_contents')
  183. ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
  184. ->willReturn('key');
  185. $this->view->expects($this->once())
  186. ->method('file_exists')
  187. ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
  188. ->willReturn(true);
  189. $this->assertSame('key',
  190. $this->storage->getSystemUserKey('shareKey_56884', 'encModule')
  191. );
  192. }
  193. public function testGetUserKey() {
  194. $this->view->expects($this->once())
  195. ->method('file_get_contents')
  196. ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
  197. ->willReturn('key');
  198. $this->view->expects($this->once())
  199. ->method('file_exists')
  200. ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
  201. ->willReturn(true);
  202. $this->assertSame('key',
  203. $this->storage->getUserKey('user1', 'publicKey', 'encModule')
  204. );
  205. }
  206. public function testDeleteUserKey() {
  207. $this->view->expects($this->once())
  208. ->method('file_exists')
  209. ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
  210. ->willReturn(true);
  211. $this->view->expects($this->once())
  212. ->method('unlink')
  213. ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
  214. ->willReturn(true);
  215. $this->assertTrue(
  216. $this->storage->deleteUserKey('user1', 'publicKey', 'encModule')
  217. );
  218. }
  219. public function testDeleteSystemUserKey() {
  220. $this->view->expects($this->once())
  221. ->method('file_exists')
  222. ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
  223. ->willReturn(true);
  224. $this->view->expects($this->once())
  225. ->method('unlink')
  226. ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
  227. ->willReturn(true);
  228. $this->assertTrue(
  229. $this->storage->deleteSystemUserKey('shareKey_56884', 'encModule')
  230. );
  231. }
  232. public function testDeleteFileKeySystemWide() {
  233. $this->util->expects($this->any())
  234. ->method('getUidAndFilename')
  235. ->willReturn(array('user1', '/files/foo.txt'));
  236. $this->util->expects($this->any())
  237. ->method('stripPartialFileExtension')
  238. ->willReturnArgument(0);
  239. $this->util->expects($this->any())
  240. ->method('isSystemWideMountPoint')
  241. ->willReturn(true);
  242. $this->view->expects($this->once())
  243. ->method('file_exists')
  244. ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  245. ->willReturn(true);
  246. $this->view->expects($this->once())
  247. ->method('unlink')
  248. ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  249. ->willReturn(true);
  250. $this->assertTrue(
  251. $this->storage->deleteFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
  252. );
  253. }
  254. public function testDeleteFileKey() {
  255. $this->util->expects($this->any())
  256. ->method('getUidAndFilename')
  257. ->willReturn(array('user1', '/files/foo.txt'));
  258. $this->util->expects($this->any())
  259. ->method('stripPartialFileExtension')
  260. ->willReturnArgument(0);
  261. $this->util->expects($this->any())
  262. ->method('isSystemWideMountPoint')
  263. ->willReturn(false);
  264. $this->view->expects($this->once())
  265. ->method('file_exists')
  266. ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  267. ->willReturn(true);
  268. $this->view->expects($this->once())
  269. ->method('unlink')
  270. ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
  271. ->willReturn(true);
  272. $this->assertTrue(
  273. $this->storage->deleteFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
  274. );
  275. }
  276. /**
  277. * @dataProvider dataProviderCopyRename
  278. */
  279. public function testRenameKeys($source, $target, $systemWideMountSource, $systemWideMountTarget, $expectedSource, $expectedTarget) {
  280. $this->view->expects($this->any())
  281. ->method('file_exists')
  282. ->willReturn(true);
  283. $this->view->expects($this->any())
  284. ->method('is_dir')
  285. ->willReturn(true);
  286. $this->view->expects($this->once())
  287. ->method('rename')
  288. ->with(
  289. $this->equalTo($expectedSource),
  290. $this->equalTo($expectedTarget))
  291. ->willReturn(true);
  292. $this->util->expects($this->any())
  293. ->method('getUidAndFilename')
  294. ->will($this->returnCallback(array($this, 'getUidAndFilenameCallback')));
  295. $this->util->expects($this->any())
  296. ->method('isSystemWideMountPoint')
  297. ->willReturnCallback(function($path, $owner) use ($systemWideMountSource, $systemWideMountTarget) {
  298. if(strpos($path, 'source.txt') !== false) {
  299. return $systemWideMountSource;
  300. }
  301. return $systemWideMountTarget;
  302. });
  303. $this->storage->renameKeys($source, $target);
  304. }
  305. /**
  306. * @dataProvider dataProviderCopyRename
  307. */
  308. public function testCopyKeys($source, $target, $systemWideMountSource, $systemWideMountTarget , $expectedSource, $expectedTarget) {
  309. $this->view->expects($this->any())
  310. ->method('file_exists')
  311. ->willReturn(true);
  312. $this->view->expects($this->any())
  313. ->method('is_dir')
  314. ->willReturn(true);
  315. $this->view->expects($this->once())
  316. ->method('copy')
  317. ->with(
  318. $this->equalTo($expectedSource),
  319. $this->equalTo($expectedTarget))
  320. ->willReturn(true);
  321. $this->util->expects($this->any())
  322. ->method('getUidAndFilename')
  323. ->will($this->returnCallback(array($this, 'getUidAndFilenameCallback')));
  324. $this->util->expects($this->any())
  325. ->method('isSystemWideMountPoint')
  326. ->willReturnCallback(function($path, $owner) use ($systemWideMountSource, $systemWideMountTarget) {
  327. if(strpos($path, 'source.txt') !== false) {
  328. return $systemWideMountSource;
  329. }
  330. return $systemWideMountTarget;
  331. });
  332. $this->storage->copyKeys($source, $target);
  333. }
  334. public function getUidAndFilenameCallback() {
  335. $args = func_get_args();
  336. $path = $args[0];
  337. $parts = explode('/', $path);
  338. return array($parts[1], '/' . implode('/', array_slice($parts, 2)));
  339. }
  340. public function dataProviderCopyRename() {
  341. return array(
  342. array('/user1/files/source.txt', '/user1/files/target.txt', false, false,
  343. '/user1/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  344. array('/user1/files/foo/source.txt', '/user1/files/target.txt', false, false,
  345. '/user1/files_encryption/keys/files/foo/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  346. array('/user1/files/source.txt', '/user1/files/foo/target.txt', false, false,
  347. '/user1/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/foo/target.txt/'),
  348. array('/user1/files/source.txt', '/user1/files/foo/target.txt', true, true,
  349. '/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/foo/target.txt/'),
  350. array('/user1/files/source.txt', '/user1/files/target.txt', false, true,
  351. '/user1/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/target.txt/'),
  352. array('/user1/files/source.txt', '/user1/files/target.txt', true, false,
  353. '/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  354. array('/user2/files/source.txt', '/user1/files/target.txt', false, false,
  355. '/user2/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  356. array('/user2/files/foo/source.txt', '/user1/files/target.txt', false, false,
  357. '/user2/files_encryption/keys/files/foo/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  358. array('/user2/files/source.txt', '/user1/files/foo/target.txt', false, false,
  359. '/user2/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/foo/target.txt/'),
  360. array('/user2/files/source.txt', '/user1/files/foo/target.txt', true, true,
  361. '/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/foo/target.txt/'),
  362. array('/user2/files/source.txt', '/user1/files/target.txt', false, true,
  363. '/user2/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/target.txt/'),
  364. array('/user2/files/source.txt', '/user1/files/target.txt', true, false,
  365. '/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'),
  366. );
  367. }
  368. /**
  369. * @dataProvider dataTestGetPathToKeys
  370. *
  371. * @param string $path
  372. * @param boolean $systemWideMountPoint
  373. * @param string $storageRoot
  374. * @param string $expected
  375. */
  376. public function testGetPathToKeys($path, $systemWideMountPoint, $storageRoot, $expected) {
  377. $this->invokePrivate($this->storage, 'root_dir', [$storageRoot]);
  378. $this->util->expects($this->any())
  379. ->method('getUidAndFilename')
  380. ->will($this->returnCallback(array($this, 'getUidAndFilenameCallback')));
  381. $this->util->expects($this->any())
  382. ->method('isSystemWideMountPoint')
  383. ->willReturn($systemWideMountPoint);
  384. $this->assertSame($expected,
  385. self::invokePrivate($this->storage, 'getPathToKeys', [$path])
  386. );
  387. }
  388. public function dataTestGetPathToKeys() {
  389. return [
  390. ['/user1/files/source.txt', false, '', '/user1/files_encryption/keys/files/source.txt/'],
  391. ['/user1/files/source.txt', true, '', '/files_encryption/keys/files/source.txt/'],
  392. ['/user1/files/source.txt', false, 'storageRoot', '/storageRoot/user1/files_encryption/keys/files/source.txt/'],
  393. ['/user1/files/source.txt', true, 'storageRoot', '/storageRoot/files_encryption/keys/files/source.txt/'],
  394. ];
  395. }
  396. public function testKeySetPreparation() {
  397. $this->view->expects($this->any())
  398. ->method('file_exists')
  399. ->willReturn(false);
  400. $this->view->expects($this->any())
  401. ->method('is_dir')
  402. ->willReturn(false);
  403. $this->view->expects($this->any())
  404. ->method('mkdir')
  405. ->will($this->returnCallback(array($this, 'mkdirCallback')));
  406. $this->mkdirStack = array(
  407. '/user1/files_encryption/keys/foo',
  408. '/user1/files_encryption/keys',
  409. '/user1/files_encryption',
  410. '/user1');
  411. self::invokePrivate($this->storage, 'keySetPreparation', array('/user1/files_encryption/keys/foo'));
  412. }
  413. public function mkdirCallback() {
  414. $args = func_get_args();
  415. $expected = array_pop($this->mkdirStack);
  416. $this->assertSame($expected, $args[0]);
  417. }
  418. /**
  419. * @dataProvider dataTestGetFileKeyDir
  420. *
  421. * @param bool $isSystemWideMountPoint
  422. * @param string $storageRoot
  423. * @param string $expected
  424. */
  425. public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected) {
  426. $path = '/user1/files/foo/bar.txt';
  427. $owner = 'user1';
  428. $relativePath = '/foo/bar.txt';
  429. $this->invokePrivate($this->storage, 'root_dir', [$storageRoot]);
  430. $this->util->expects($this->once())->method('isSystemWideMountPoint')
  431. ->willReturn($isSystemWideMountPoint);
  432. $this->util->expects($this->once())->method('getUidAndFilename')
  433. ->with($path)->willReturn([$owner, $relativePath]);
  434. $this->assertSame($expected,
  435. $this->invokePrivate($this->storage, 'getFileKeyDir', ['OC_DEFAULT_MODULE', $path])
  436. );
  437. }
  438. public function dataTestGetFileKeyDir() {
  439. return [
  440. [false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
  441. [true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
  442. [false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
  443. [true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
  444. ];
  445. }
  446. /**
  447. * @dataProvider dataTestBackupUserKeys
  448. * @param bool $createBackupDir
  449. */
  450. public function testBackupUserKeys($createBackupDir) {
  451. $storage = $this->getMockBuilder('OC\Encryption\Keys\Storage')
  452. ->setConstructorArgs([$this->view, $this->util])
  453. ->setMethods(['getTimestamp'])
  454. ->getMock();
  455. $storage->expects($this->any())->method('getTimestamp')->willReturn('1234567');
  456. $this->view->expects($this->once())->method('file_exists')
  457. ->with('user1/files_encryption/backup')->willReturn(!$createBackupDir);
  458. if ($createBackupDir) {
  459. $this->view->expects($this->at(1))->method('mkdir')
  460. ->with('user1/files_encryption/backup');
  461. $this->view->expects($this->at(2))->method('mkdir')
  462. ->with('user1/files_encryption/backup/test.encryptionModule.1234567');
  463. } else {
  464. $this->view->expects($this->once())->method('mkdir')
  465. ->with('user1/files_encryption/backup/test.encryptionModule.1234567');
  466. }
  467. $this->view->expects($this->once())->method('copy')
  468. ->with(
  469. 'user1/files_encryption/encryptionModule',
  470. 'user1/files_encryption/backup/test.encryptionModule.1234567'
  471. )->willReturn(true);
  472. $this->assertTrue($storage->backupUserKeys('encryptionModule', 'test', 'user1'));
  473. }
  474. public function dataTestBackupUserKeys() {
  475. return [
  476. [true], [false]
  477. ];
  478. }
  479. }