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.

util.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. <?php
  2. /**
  3. * @author Andreas Fischer <bantu@owncloud.com>
  4. * @author Björn Schießle <schiessle@owncloud.com>
  5. * @author Florin Peter <github@florin-peter.de>
  6. * @author Joas Schilling <nickvergessen@owncloud.com>
  7. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  8. * @author Markus Goetz <markus@woboq.com>
  9. * @author Morris Jobke <hey@morrisjobke.de>
  10. * @author Robin Appelman <icewind@owncloud.com>
  11. * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
  12. * @author Sam Tuke <mail@samtuke.com>
  13. * @author Thomas Müller <thomas.mueller@tmit.eu>
  14. * @author Vincent Petry <pvince81@owncloud.com>
  15. *
  16. * @copyright Copyright (c) 2015, ownCloud, Inc.
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OCA\Files_Encryption\Tests;
  33. /**
  34. * Class Util
  35. */
  36. class Util extends TestCase {
  37. const TEST_ENCRYPTION_UTIL_USER1 = "test-util-user1";
  38. const TEST_ENCRYPTION_UTIL_USER2 = "test-util-user2";
  39. const TEST_ENCRYPTION_UTIL_GROUP1 = "test-util-group1";
  40. const TEST_ENCRYPTION_UTIL_GROUP2 = "test-util-group2";
  41. const TEST_ENCRYPTION_UTIL_LEGACY_USER = "test-legacy-user";
  42. public $userId;
  43. public $encryptionDir;
  44. public $publicKeyDir;
  45. public $pass;
  46. /**
  47. * @var \OC\Files\View
  48. */
  49. public $view;
  50. public $keysPath;
  51. public $publicKeyPath;
  52. public $privateKeyPath;
  53. /**
  54. * @var \OCA\Files_Encryption\Util
  55. */
  56. public $util;
  57. public $dataShort;
  58. public $legacyEncryptedData;
  59. public $legacyEncryptedDataKey;
  60. public $legacyKey;
  61. public $stateFilesTrashbin;
  62. public static function setUpBeforeClass() {
  63. parent::setUpBeforeClass();
  64. // create test user
  65. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1, true);
  66. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER2, true);
  67. self::loginHelper(self::TEST_ENCRYPTION_UTIL_LEGACY_USER, true);
  68. // create groups
  69. \OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
  70. \OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
  71. // add user 1 to group1
  72. \OC_Group::addToGroup(self::TEST_ENCRYPTION_UTIL_USER1, self::TEST_ENCRYPTION_UTIL_GROUP1);
  73. }
  74. protected function setUp() {
  75. parent::setUp();
  76. // login user
  77. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
  78. \OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);
  79. $this->userId = self::TEST_ENCRYPTION_UTIL_USER1;
  80. $this->pass = self::TEST_ENCRYPTION_UTIL_USER1;
  81. // set content for encrypting / decrypting in tests
  82. $this->dataUrl = __DIR__ . '/../lib/crypt.php';
  83. $this->dataShort = 'hats';
  84. $this->dataLong = file_get_contents(__DIR__ . '/../lib/crypt.php');
  85. $this->legacyData = __DIR__ . '/legacy-text.txt';
  86. $this->legacyEncryptedData = __DIR__ . '/legacy-encrypted-text.txt';
  87. $this->legacyEncryptedDataKey = __DIR__ . '/encryption.key';
  88. $this->legacyKey = "30943623843030686906\0\0\0\0";
  89. $keypair = \OCA\Files_Encryption\Crypt::createKeypair();
  90. $this->genPublicKey = $keypair['publicKey'];
  91. $this->genPrivateKey = $keypair['privateKey'];
  92. $this->publicKeyDir = \OCA\Files_Encryption\Keymanager::getPublicKeyPath();
  93. $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
  94. $this->keysPath = $this->encryptionDir . '/' . 'keys';
  95. $this->publicKeyPath =
  96. $this->publicKeyDir . '/' . $this->userId . '.publicKey'; // e.g. data/public-keys/admin.publicKey
  97. $this->privateKeyPath =
  98. $this->encryptionDir . '/' . $this->userId . '.privateKey'; // e.g. data/admin/admin.privateKey
  99. $this->view = new \OC\Files\View('/');
  100. $this->util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  101. // remember files_trashbin state
  102. $this->stateFilesTrashbin = \OC_App::isEnabled('files_trashbin');
  103. // we don't want to tests with app files_trashbin enabled
  104. \OC_App::disable('files_trashbin');
  105. }
  106. protected function tearDown() {
  107. // reset app files_trashbin
  108. if ($this->stateFilesTrashbin) {
  109. \OC_App::enable('files_trashbin');
  110. }
  111. else {
  112. \OC_App::disable('files_trashbin');
  113. }
  114. parent::tearDown();
  115. }
  116. public static function tearDownAfterClass() {
  117. // cleanup test user
  118. \OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER1);
  119. \OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER2);
  120. \OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_LEGACY_USER);
  121. //cleanup groups
  122. \OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
  123. \OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
  124. parent::tearDownAfterClass();
  125. }
  126. /**
  127. * @medium
  128. * test that paths set during User construction are correct
  129. */
  130. function testKeyPaths() {
  131. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  132. $this->assertEquals($this->publicKeyDir, $util->getPath('publicKeyDir'));
  133. $this->assertEquals($this->encryptionDir, $util->getPath('encryptionDir'));
  134. $this->assertEquals($this->keysPath, $util->getPath('keysPath'));
  135. $this->assertEquals($this->publicKeyPath, $util->getPath('publicKeyPath'));
  136. $this->assertEquals($this->privateKeyPath, $util->getPath('privateKeyPath'));
  137. }
  138. /**
  139. * @medium
  140. * test detection of encrypted files
  141. */
  142. function testIsEncryptedPath() {
  143. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  144. self::loginHelper($this->userId);
  145. $unencryptedFile = '/tmpUnencrypted-' . $this->getUniqueID() . '.txt';
  146. $encryptedFile = '/tmpEncrypted-' . $this->getUniqueID() . '.txt';
  147. // Disable encryption proxy to write a unencrypted file
  148. $proxyStatus = \OC_FileProxy::$enabled;
  149. \OC_FileProxy::$enabled = false;
  150. $this->view->file_put_contents($this->userId . '/files/' . $unencryptedFile, $this->dataShort);
  151. // Re-enable proxy - our work is done
  152. \OC_FileProxy::$enabled = $proxyStatus;
  153. // write a encrypted file
  154. $this->view->file_put_contents($this->userId . '/files/' . $encryptedFile, $this->dataShort);
  155. // test if both files are detected correctly
  156. $this->assertFalse($util->isEncryptedPath($this->userId . '/files/' . $unencryptedFile));
  157. $this->assertTrue($util->isEncryptedPath($this->userId . '/files/' . $encryptedFile));
  158. // cleanup
  159. $this->view->unlink($this->userId . '/files/' . $unencryptedFile);
  160. $this->view->unlink($this->userId . '/files/' . $encryptedFile);
  161. }
  162. /**
  163. * @medium
  164. * test setup of encryption directories
  165. */
  166. function testSetupServerSide() {
  167. $this->assertEquals(true, $this->util->setupServerSide($this->pass));
  168. }
  169. /**
  170. * @medium
  171. * test checking whether account is ready for encryption,
  172. */
  173. function testUserIsReady() {
  174. $this->assertEquals(true, $this->util->ready());
  175. }
  176. /**
  177. * test checking whether account is not ready for encryption,
  178. */
  179. // function testUserIsNotReady() {
  180. // $this->view->unlink($this->publicKeyDir);
  181. //
  182. // $params['uid'] = $this->userId;
  183. // $params['password'] = $this->pass;
  184. // $this->assertFalse(OCA\Files_Encryption\Hooks::login($params));
  185. //
  186. // $this->view->unlink($this->privateKeyPath);
  187. // }
  188. /**
  189. * @medium
  190. */
  191. function testRecoveryEnabledForUser() {
  192. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  193. // Record the value so we can return it to it's original state later
  194. $enabled = $util->recoveryEnabledForUser();
  195. $this->assertTrue($util->setRecoveryForUser(!$enabled));
  196. $this->assertEquals(!$enabled, $util->recoveryEnabledForUser());
  197. $this->assertTrue($util->setRecoveryForUser($enabled));
  198. $this->assertEquals($enabled, $util->recoveryEnabledForUser());
  199. }
  200. /**
  201. * @medium
  202. */
  203. function testGetUidAndFilename() {
  204. \OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);
  205. $filename = '/tmp-' . $this->getUniqueID() . '.test';
  206. // Disable encryption proxy to prevent recursive calls
  207. $proxyStatus = \OC_FileProxy::$enabled;
  208. \OC_FileProxy::$enabled = false;
  209. $this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
  210. // Re-enable proxy - our work is done
  211. \OC_FileProxy::$enabled = $proxyStatus;
  212. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  213. list($fileOwnerUid, $file) = $util->getUidAndFilename($filename);
  214. $this->assertEquals(self::TEST_ENCRYPTION_UTIL_USER1, $fileOwnerUid);
  215. $this->assertEquals($file, $filename);
  216. $this->view->unlink($this->userId . '/files/' . $filename);
  217. }
  218. /**
  219. * Test that data that is read by the crypto stream wrapper
  220. */
  221. function testGetFileSize() {
  222. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
  223. $filename = 'tmp-' . $this->getUniqueID();
  224. $externalFilename = '/' . $this->userId . '/files/' . $filename;
  225. // Test for 0 byte files
  226. $problematicFileSizeData = "";
  227. $cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
  228. $this->assertTrue(is_int($cryptedFile));
  229. $this->assertEquals($this->util->getFileSize($externalFilename), 0);
  230. $decrypt = $this->view->file_get_contents($externalFilename);
  231. $this->assertEquals($problematicFileSizeData, $decrypt);
  232. $this->view->unlink($this->userId . '/files/' . $filename);
  233. // Test a file with 18377 bytes as in https://github.com/owncloud/mirall/issues/1009
  234. $problematicFileSizeData = str_pad("", 18377, "abc");
  235. $cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
  236. $this->assertTrue(is_int($cryptedFile));
  237. $this->assertEquals($this->util->getFileSize($externalFilename), 18377);
  238. $decrypt = $this->view->file_get_contents($externalFilename);
  239. $this->assertEquals($problematicFileSizeData, $decrypt);
  240. $this->view->unlink($this->userId . '/files/' . $filename);
  241. }
  242. function testEncryptAll() {
  243. $filename = "/encryptAll" . $this->getUniqueID() . ".txt";
  244. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  245. // disable encryption to upload a unencrypted file
  246. \OC_App::disable('files_encryption');
  247. $this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
  248. $fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
  249. $this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo);
  250. // enable file encryption again
  251. \OC_App::enable('files_encryption');
  252. // encrypt all unencrypted files
  253. $util->encryptAll('/' . $this->userId . '/' . 'files');
  254. $fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
  255. $this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo);
  256. // check if mtime and etags unchanged
  257. $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']);
  258. $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']);
  259. $this->view->unlink($this->userId . '/files/' . $filename);
  260. }
  261. function testDecryptAll() {
  262. $filename = "/decryptAll" . $this->getUniqueID() . ".txt";
  263. $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/');
  264. $userdir = $datadir . '/' . $this->userId . '/files/';
  265. $this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
  266. $fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
  267. $this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo);
  268. $this->assertEquals($fileInfoEncrypted['encrypted'], 1);
  269. $encContent = file_get_contents($userdir . $filename);
  270. \OC_App::disable('files_encryption');
  271. $user = \OCP\User::getUser();
  272. $this->logoutHelper();
  273. $this->loginHelper($user, false, false, false);
  274. $content = file_get_contents($userdir . $filename);
  275. //content should be encrypted
  276. $this->assertSame($encContent, $content);
  277. // now we load the encryption app again
  278. \OC_App::loadApp('files_encryption');
  279. // init encryption app
  280. $params = array('uid' => \OCP\User::getUser(),
  281. 'password' => \OCP\User::getUser());
  282. $view = new \OC\Files\View('/');
  283. $util = new \OCA\Files_Encryption\Util($view, \OCP\User::getUser());
  284. $result = $util->initEncryption($params);
  285. $this->assertTrue($result instanceof \OCA\Files_Encryption\Session);
  286. $successful = $util->decryptAll();
  287. $this->assertTrue($successful);
  288. $this->logoutHelper();
  289. $this->loginHelper($user, false, false, false);
  290. // file should be unencrypted and fileInfo should contain the correct values
  291. $content = file_get_contents($userdir . $filename);
  292. // now we should get the plain data
  293. $this->assertSame($this->dataShort, $content);
  294. $fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
  295. $this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo);
  296. // check if mtime and etags unchanged
  297. $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']);
  298. $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']);
  299. // file should no longer be encrypted
  300. $this->assertEquals(0, $fileInfoUnencrypted['encrypted']);
  301. $backupPath = $this->getBackupPath('decryptAll');
  302. // check if the keys where moved to the backup location
  303. $this->assertTrue($this->view->is_dir($backupPath . '/keys'));
  304. $this->assertTrue($this->view->file_exists($backupPath . '/keys/' . $filename . '/fileKey'));
  305. $this->assertTrue($this->view->file_exists($backupPath . '/keys/' . $filename . '/' . $user . '.shareKey'));
  306. // cleanup
  307. $this->view->unlink($this->userId . '/files/' . $filename);
  308. $this->view->deleteAll($backupPath);
  309. \OC_App::enable('files_encryption');
  310. }
  311. private function createDummyKeysForBackupTest() {
  312. // create some dummy key files
  313. $encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
  314. $this->view->mkdir($encPath . '/keys/foo');
  315. $this->view->file_put_contents($encPath . '/keys/foo/fileKey', 'key');
  316. $this->view->file_put_contents($encPath . '/keys/foo/user1.shareKey', 'share key');
  317. }
  318. /**
  319. * test if all keys get moved to the backup folder correctly
  320. *
  321. * @dataProvider dataBackupAllKeys
  322. */
  323. function testBackupAllKeys($addTimestamp, $includeUserKeys) {
  324. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
  325. $this->createDummyKeysForBackupTest();
  326. $util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
  327. $util->backupAllKeys('testBackupAllKeys', $addTimestamp, $includeUserKeys);
  328. $backupPath = $this->getBackupPath('testBackupAllKeys');
  329. // check backupDir Content
  330. $this->assertTrue($this->view->is_dir($backupPath . '/keys'));
  331. $this->assertTrue($this->view->is_dir($backupPath . '/keys/foo'));
  332. $this->assertTrue($this->view->file_exists($backupPath . '/keys/foo/fileKey'));
  333. $this->assertTrue($this->view->file_exists($backupPath . '/keys/foo/user1.shareKey'));
  334. if ($includeUserKeys) {
  335. $this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.privateKey'));
  336. $this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.publicKey'));
  337. } else {
  338. $this->assertFalse($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.privateKey'));
  339. $this->assertFalse($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.publicKey'));
  340. }
  341. //cleanup
  342. $this->view->deleteAll($backupPath);
  343. $this->view->unlink($this->encryptionDir . '/keys/foo/fileKey');
  344. $this->view->unlink($this->encryptionDir . '/keys/foo/user1.shareKey');
  345. }
  346. function dataBackupAllKeys() {
  347. return array(
  348. array(true, true),
  349. array(false, true),
  350. array(true, false),
  351. array(false, false),
  352. );
  353. }
  354. /**
  355. * @dataProvider dataBackupAllKeys
  356. */
  357. function testRestoreBackup($addTimestamp, $includeUserKeys) {
  358. $util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
  359. $this->createDummyKeysForBackupTest();
  360. $util->backupAllKeys('restoreKeysBackupTest', $addTimestamp, $includeUserKeys);
  361. $this->view->deleteAll($this->keysPath);
  362. if ($includeUserKeys) {
  363. $this->view->unlink($this->privateKeyPath);
  364. $this->view->unlink($this->publicKeyPath);
  365. }
  366. // key should be removed after backup was created
  367. $this->assertFalse($this->view->is_dir($this->keysPath));
  368. if ($includeUserKeys) {
  369. $this->assertFalse($this->view->file_exists($this->privateKeyPath));
  370. $this->assertFalse($this->view->file_exists($this->publicKeyPath));
  371. }
  372. $backupPath = $this->getBackupPath('restoreKeysBackupTest');
  373. $backupName = substr(basename($backupPath), strlen('backup.'));
  374. $this->assertTrue($util->restoreBackup($backupName));
  375. // check if all keys are restored
  376. $this->assertFalse($this->view->is_dir($backupPath));
  377. $this->assertTrue($this->view->is_dir($this->keysPath));
  378. $this->assertTrue($this->view->is_dir($this->keysPath . '/foo'));
  379. $this->assertTrue($this->view->file_exists($this->keysPath . '/foo/fileKey'));
  380. $this->assertTrue($this->view->file_exists($this->keysPath . '/foo/user1.shareKey'));
  381. $this->assertTrue($this->view->file_exists($this->privateKeyPath));
  382. $this->assertTrue($this->view->file_exists($this->publicKeyPath));
  383. }
  384. function testDeleteBackup() {
  385. $util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
  386. $this->createDummyKeysForBackupTest();
  387. $util->backupAllKeys('testDeleteBackup', false, false);
  388. $this->assertTrue($this->view->is_dir($this->encryptionDir . '/backup.testDeleteBackup'));
  389. $util->deleteBackup('testDeleteBackup');
  390. $this->assertFalse($this->view->is_dir($this->encryptionDir . '/backup.testDeleteBackup'));
  391. }
  392. function testDescryptAllWithBrokenFiles() {
  393. $file1 = "/decryptAll1" . $this->getUniqueID() . ".txt";
  394. $file2 = "/decryptAll2" . $this->getUniqueID() . ".txt";
  395. $util = new \OCA\Files_Encryption\Util($this->view, $this->userId);
  396. $this->view->file_put_contents($this->userId . '/files/' . $file1, $this->dataShort);
  397. $this->view->file_put_contents($this->userId . '/files/' . $file2, $this->dataShort);
  398. $fileInfoEncrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
  399. $fileInfoEncrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
  400. $this->assertTrue($fileInfoEncrypted1 instanceof \OC\Files\FileInfo);
  401. $this->assertTrue($fileInfoEncrypted2 instanceof \OC\Files\FileInfo);
  402. $this->assertEquals($fileInfoEncrypted1['encrypted'], 1);
  403. $this->assertEquals($fileInfoEncrypted2['encrypted'], 1);
  404. // rename keyfile for file1 so that the decryption for file1 fails
  405. // Expected behaviour: decryptAll() returns false, file2 gets decrypted anyway
  406. $this->view->rename($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey',
  407. $this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved');
  408. // need to reset key cache that we don't use the cached key
  409. $this->resetKeyCache();
  410. // decrypt all encrypted files
  411. $result = $util->decryptAll();
  412. $this->assertFalse($result);
  413. $fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
  414. $fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
  415. $this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo);
  416. $this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo);
  417. // file1 should be still encrypted; file2 should be decrypted
  418. $this->assertEquals(1, $fileInfoUnencrypted1['encrypted']);
  419. $this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);
  420. // keyfiles and share keys should still exist
  421. $this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/keys/'));
  422. $this->assertTrue($this->view->file_exists($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved'));
  423. $this->assertTrue($this->view->file_exists($this->userId . '/files_encryption/keys/' . $file1 . '/' . $this->userId . '.shareKey'));
  424. // rename the keyfile for file1 back
  425. $this->view->rename($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved',
  426. $this->userId . '/files_encryption/keys/' . $file1 . '/fileKey');
  427. // try again to decrypt all encrypted files
  428. $result = $util->decryptAll();
  429. $this->assertTrue($result);
  430. $fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
  431. $fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
  432. $this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo);
  433. $this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo);
  434. // now both files should be decrypted
  435. $this->assertEquals(0, $fileInfoUnencrypted1['encrypted']);
  436. $this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);
  437. // keyfiles and share keys should be deleted
  438. $this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/keys/'));
  439. //cleanup
  440. $backupPath = $this->getBackupPath('decryptAll');
  441. $this->view->unlink($this->userId . '/files/' . $file1);
  442. $this->view->unlink($this->userId . '/files/' . $file2);
  443. $this->view->deleteAll($backupPath);
  444. }
  445. function getBackupPath($extension) {
  446. $encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
  447. $encFolderContent = $this->view->getDirectoryContent($encPath);
  448. $backupPath = '';
  449. foreach ($encFolderContent as $c) {
  450. $name = $c['name'];
  451. if (substr($name, 0, strlen('backup.' . $extension)) === 'backup.' . $extension) {
  452. $backupPath = $encPath . '/'. $c['name'];
  453. break;
  454. }
  455. }
  456. return $backupPath;
  457. }
  458. /**
  459. * @dataProvider dataProviderFortestIsMountPointApplicableToUser
  460. */
  461. function testIsMountPointApplicableToUser($mount, $expectedResult) {
  462. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
  463. $dummyClass = new DummyUtilClass($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
  464. $result = $dummyClass->testIsMountPointApplicableToUser($mount);
  465. $this->assertSame($expectedResult, $result);
  466. }
  467. function dataProviderFortestIsMountPointApplicableToUser() {
  468. return array(
  469. array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER1))), true),
  470. array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
  471. array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array())), true),
  472. array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), true),
  473. array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
  474. array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2, 'all'))), true),
  475. array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array('all'))), true),
  476. );
  477. }
  478. /**
  479. * Tests that filterShareReadyUsers() returns the correct list of
  480. * users that are ready or not ready for encryption
  481. */
  482. public function testFilterShareReadyUsers() {
  483. $appConfig = \OC::$server->getAppConfig();
  484. $publicShareKeyId = $appConfig->getValue('files_encryption', 'publicShareKeyId');
  485. $recoveryKeyId = $appConfig->getValue('files_encryption', 'recoveryKeyId');
  486. $usersToTest = array(
  487. 'readyUser',
  488. 'notReadyUser',
  489. 'nonExistingUser',
  490. $publicShareKeyId,
  491. $recoveryKeyId,
  492. );
  493. self::loginHelper('readyUser', true);
  494. self::loginHelper('notReadyUser', true);
  495. // delete encryption dir to make it not ready
  496. $this->view->unlink('notReadyUser/files_encryption/');
  497. // login as user1
  498. self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
  499. $result = $this->util->filterShareReadyUsers($usersToTest);
  500. $this->assertEquals(
  501. array('readyUser', $publicShareKeyId, $recoveryKeyId),
  502. $result['ready']
  503. );
  504. $this->assertEquals(
  505. array('notReadyUser', 'nonExistingUser'),
  506. $result['unready']
  507. );
  508. \OC_User::deleteUser('readyUser');
  509. }
  510. /**
  511. * helper function to set migration status to the right value
  512. * to be able to test the migration path
  513. *
  514. * @param integer $status needed migration status for test
  515. * @param string $user for which user the status should be set
  516. * @return boolean
  517. */
  518. private function setMigrationStatus($status, $user) {
  519. \OC::$server->getConfig()->setUserValue($user, 'files_encryption', 'migration_status', (string)$status);
  520. // the update will definitely be executed -> return value is always true
  521. return true;
  522. }
  523. }
  524. /**
  525. * dummy class extends \OCA\Files_Encryption\Util to access protected methods for testing
  526. */
  527. class DummyUtilClass extends \OCA\Files_Encryption\Util {
  528. public function testIsMountPointApplicableToUser($mount) {
  529. return $this->isMountPointApplicableToUser($mount);
  530. }
  531. }