DecryptAllTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <?php
  2. /**
  3. * @author Björn Schießle <schiessle@owncloud.com>
  4. *
  5. * @copyright Copyright (c) 2015, ownCloud, Inc.
  6. * @license AGPL-3.0
  7. *
  8. * This code is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License, version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License, version 3,
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>
  19. *
  20. */
  21. namespace Test\Encryption;
  22. use OC\Encryption\DecryptAll;
  23. use OC\Encryption\Exceptions\DecryptionFailedException;
  24. use OC\Encryption\Manager;
  25. use OC\Files\FileInfo;
  26. use OC\Files\View;
  27. use OCP\Files\Storage;
  28. use OCP\IUserManager;
  29. use OCP\UserInterface;
  30. use Symfony\Component\Console\Formatter\OutputFormatterInterface;
  31. use Symfony\Component\Console\Helper\ProgressBar;
  32. use Symfony\Component\Console\Input\InputInterface;
  33. use Symfony\Component\Console\Output\OutputInterface;
  34. use Test\TestCase;
  35. /**
  36. * Class DecryptAllTest
  37. *
  38. * @group DB
  39. *
  40. * @package Test\Encryption
  41. */
  42. class DecryptAllTest extends TestCase {
  43. /** @var \PHPUnit\Framework\MockObject\MockObject | IUserManager */
  44. protected $userManager;
  45. /** @var \PHPUnit\Framework\MockObject\MockObject | Manager */
  46. protected $encryptionManager;
  47. /** @var \PHPUnit\Framework\MockObject\MockObject | View */
  48. protected $view;
  49. /** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Input\InputInterface */
  50. protected $inputInterface;
  51. /** @var \PHPUnit\Framework\MockObject\MockObject | \Symfony\Component\Console\Output\OutputInterface */
  52. protected $outputInterface;
  53. /** @var \PHPUnit\Framework\MockObject\MockObject | \OCP\UserInterface */
  54. protected $userInterface;
  55. /** @var DecryptAll */
  56. protected $instance;
  57. protected function setUp(): void {
  58. parent::setUp();
  59. $this->userManager = $this->getMockBuilder(IUserManager::class)
  60. ->disableOriginalConstructor()->getMock();
  61. $this->encryptionManager = $this->getMockBuilder('OC\Encryption\Manager')
  62. ->disableOriginalConstructor()->getMock();
  63. $this->view = $this->getMockBuilder(View::class)
  64. ->disableOriginalConstructor()->getMock();
  65. $this->inputInterface = $this->getMockBuilder(InputInterface::class)
  66. ->disableOriginalConstructor()->getMock();
  67. $this->outputInterface = $this->getMockBuilder(OutputInterface::class)
  68. ->disableOriginalConstructor()->getMock();
  69. $this->outputInterface->expects($this->any())->method('isDecorated')
  70. ->willReturn(false);
  71. $this->userInterface = $this->getMockBuilder(UserInterface::class)
  72. ->disableOriginalConstructor()->getMock();
  73. /* We need format method to return a string */
  74. $outputFormatter = $this->createMock(OutputFormatterInterface::class);
  75. $outputFormatter->method('format')->willReturn('foo');
  76. $outputFormatter->method('isDecorated')->willReturn(false);
  77. $this->outputInterface->expects($this->any())->method('getFormatter')
  78. ->willReturn($outputFormatter);
  79. $this->instance = new DecryptAll($this->encryptionManager, $this->userManager, $this->view);
  80. $this->invokePrivate($this->instance, 'input', [$this->inputInterface]);
  81. $this->invokePrivate($this->instance, 'output', [$this->outputInterface]);
  82. }
  83. public function dataDecryptAll() {
  84. return [
  85. [true, 'user1', true],
  86. [false, 'user1', true],
  87. [true, '0', true],
  88. [false, '0', true],
  89. [true, '', false],
  90. ];
  91. }
  92. /**
  93. * @dataProvider dataDecryptAll
  94. * @param bool $prepareResult
  95. * @param string $user
  96. * @param bool $userExistsChecked
  97. */
  98. public function testDecryptAll($prepareResult, $user, $userExistsChecked) {
  99. if ($userExistsChecked) {
  100. $this->userManager->expects($this->once())->method('userExists')->willReturn(true);
  101. } else {
  102. $this->userManager->expects($this->never())->method('userExists');
  103. }
  104. /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject | $instance */
  105. $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
  106. ->setConstructorArgs(
  107. [
  108. $this->encryptionManager,
  109. $this->userManager,
  110. $this->view
  111. ]
  112. )
  113. ->setMethods(['prepareEncryptionModules', 'decryptAllUsersFiles'])
  114. ->getMock();
  115. $instance->expects($this->once())
  116. ->method('prepareEncryptionModules')
  117. ->with($user)
  118. ->willReturn($prepareResult);
  119. if ($prepareResult) {
  120. $instance->expects($this->once())
  121. ->method('decryptAllUsersFiles')
  122. ->with($user);
  123. } else {
  124. $instance->expects($this->never())->method('decryptAllUsersFiles');
  125. }
  126. $instance->decryptAll($this->inputInterface, $this->outputInterface, $user);
  127. }
  128. /**
  129. * test decrypt all call with a user who doesn't exists
  130. */
  131. public function testDecryptAllWrongUser() {
  132. $this->userManager->expects($this->once())->method('userExists')->willReturn(false);
  133. $this->outputInterface->expects($this->once())->method('writeln')
  134. ->with('User "user1" does not exist. Please check the username and try again');
  135. $this->assertFalse(
  136. $this->instance->decryptAll($this->inputInterface, $this->outputInterface, 'user1')
  137. );
  138. }
  139. public function dataTrueFalse() {
  140. return [
  141. [true],
  142. [false],
  143. ];
  144. }
  145. /**
  146. * @dataProvider dataTrueFalse
  147. * @param bool $success
  148. */
  149. public function testPrepareEncryptionModules($success) {
  150. $user = 'user1';
  151. $dummyEncryptionModule = $this->getMockBuilder('OCP\Encryption\IEncryptionModule')
  152. ->disableOriginalConstructor()->getMock();
  153. $dummyEncryptionModule->expects($this->once())
  154. ->method('prepareDecryptAll')
  155. ->with($this->inputInterface, $this->outputInterface, $user)
  156. ->willReturn($success);
  157. $callback = function () use ($dummyEncryptionModule) {
  158. return $dummyEncryptionModule;
  159. };
  160. $moduleDescription = [
  161. 'id' => 'id',
  162. 'displayName' => 'displayName',
  163. 'callback' => $callback
  164. ];
  165. $this->encryptionManager->expects($this->once())
  166. ->method('getEncryptionModules')
  167. ->willReturn([$moduleDescription]);
  168. $this->assertSame($success,
  169. $this->invokePrivate($this->instance, 'prepareEncryptionModules', [$user])
  170. );
  171. }
  172. /**
  173. * @dataProvider dataTestDecryptAllUsersFiles
  174. */
  175. public function testDecryptAllUsersFiles($user) {
  176. /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject | $instance */
  177. $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
  178. ->setConstructorArgs(
  179. [
  180. $this->encryptionManager,
  181. $this->userManager,
  182. $this->view
  183. ]
  184. )
  185. ->setMethods(['decryptUsersFiles'])
  186. ->getMock();
  187. $this->invokePrivate($instance, 'input', [$this->inputInterface]);
  188. $this->invokePrivate($instance, 'output', [$this->outputInterface]);
  189. if (empty($user)) {
  190. $this->userManager->expects($this->once())
  191. ->method('getBackends')
  192. ->willReturn([$this->userInterface]);
  193. $this->userInterface->expects($this->any())
  194. ->method('getUsers')
  195. ->willReturn(['user1', 'user2']);
  196. $instance->expects($this->exactly(2))
  197. ->method('decryptUsersFiles')
  198. ->withConsecutive(
  199. ['user1'],
  200. ['user2'],
  201. );
  202. } else {
  203. $instance->expects($this->once())
  204. ->method('decryptUsersFiles')
  205. ->with($user);
  206. }
  207. $this->invokePrivate($instance, 'decryptAllUsersFiles', [$user]);
  208. }
  209. public function dataTestDecryptAllUsersFiles() {
  210. return [
  211. ['user1'],
  212. ['']
  213. ];
  214. }
  215. public function testDecryptUsersFiles() {
  216. /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject $instance */
  217. $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
  218. ->setConstructorArgs(
  219. [
  220. $this->encryptionManager,
  221. $this->userManager,
  222. $this->view
  223. ]
  224. )
  225. ->setMethods(['decryptFile'])
  226. ->getMock();
  227. $storage = $this->getMockBuilder(Storage::class)
  228. ->disableOriginalConstructor()->getMock();
  229. $sharedStorage = $this->getMockBuilder(Storage::class)
  230. ->disableOriginalConstructor()->getMock();
  231. $sharedStorage->expects($this->once())->method('instanceOfStorage')
  232. ->with('OCA\Files_Sharing\SharedStorage')->willReturn(true);
  233. $this->view->expects($this->exactly(2))
  234. ->method('getDirectoryContent')
  235. ->withConsecutive(
  236. ['/user1/files'],
  237. ['/user1/files/foo']
  238. )
  239. ->willReturnOnConsecutiveCalls(
  240. [
  241. new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], null),
  242. new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], null),
  243. new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], null),
  244. ],
  245. [
  246. new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], null)
  247. ]
  248. );
  249. $this->view->expects($this->any())->method('is_dir')
  250. ->willReturnCallback(
  251. function ($path) {
  252. if ($path === '/user1/files/foo') {
  253. return true;
  254. }
  255. return false;
  256. }
  257. );
  258. $instance->expects($this->exactly(2))
  259. ->method('decryptFile')
  260. ->withConsecutive(
  261. ['/user1/files/bar'],
  262. ['/user1/files/foo/subfile'],
  263. );
  264. /* We need format method to return a string */
  265. $outputFormatter = $this->createMock(OutputFormatterInterface::class);
  266. $outputFormatter->method('isDecorated')->willReturn(false);
  267. $outputFormatter->method('format')->willReturn('foo');
  268. $output = $this->createMock(OutputInterface::class);
  269. $output->expects($this->any())
  270. ->method('getFormatter')
  271. ->willReturn($outputFormatter);
  272. $progressBar = new ProgressBar($output);
  273. $this->invokePrivate($instance, 'decryptUsersFiles', ['user1', $progressBar, '']);
  274. }
  275. /**
  276. * @dataProvider dataTrueFalse
  277. */
  278. public function testDecryptFile($isEncrypted) {
  279. $path = 'test.txt';
  280. /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject $instance */
  281. $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
  282. ->setConstructorArgs(
  283. [
  284. $this->encryptionManager,
  285. $this->userManager,
  286. $this->view
  287. ]
  288. )
  289. ->setMethods(['getTimestamp'])
  290. ->getMock();
  291. $fileInfo = $this->createMock(FileInfo::class);
  292. $fileInfo->expects($this->any())->method('isEncrypted')
  293. ->willReturn($isEncrypted);
  294. $this->view->expects($this->any())->method('getFileInfo')
  295. ->willReturn($fileInfo);
  296. if ($isEncrypted) {
  297. $instance->expects($this->any())->method('getTimestamp')->willReturn(42);
  298. $this->view->expects($this->once())
  299. ->method('copy')
  300. ->with($path, $path . '.decrypted.42');
  301. $this->view->expects($this->once())
  302. ->method('rename')
  303. ->with($path . '.decrypted.42', $path);
  304. } else {
  305. $instance->expects($this->never())->method('getTimestamp');
  306. $this->view->expects($this->never())->method('copy');
  307. $this->view->expects($this->never())->method('rename');
  308. }
  309. $this->assertTrue(
  310. $this->invokePrivate($instance, 'decryptFile', [$path])
  311. );
  312. }
  313. public function testDecryptFileFailure() {
  314. $path = 'test.txt';
  315. /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject $instance */
  316. $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
  317. ->setConstructorArgs(
  318. [
  319. $this->encryptionManager,
  320. $this->userManager,
  321. $this->view
  322. ]
  323. )
  324. ->setMethods(['getTimestamp'])
  325. ->getMock();
  326. $fileInfo = $this->createMock(FileInfo::class);
  327. $fileInfo->expects($this->any())->method('isEncrypted')
  328. ->willReturn(true);
  329. $this->view->expects($this->any())->method('getFileInfo')
  330. ->willReturn($fileInfo);
  331. $instance->expects($this->any())->method('getTimestamp')->willReturn(42);
  332. $this->view->expects($this->once())
  333. ->method('copy')
  334. ->with($path, $path . '.decrypted.42')
  335. ->willReturnCallback(function () {
  336. throw new DecryptionFailedException();
  337. });
  338. $this->view->expects($this->never())->method('rename');
  339. $this->view->expects($this->once())
  340. ->method('file_exists')
  341. ->with($path . '.decrypted.42')
  342. ->willReturn(true);
  343. $this->view->expects($this->once())
  344. ->method('unlink')
  345. ->with($path . '.decrypted.42');
  346. $this->assertFalse(
  347. $this->invokePrivate($instance, 'decryptFile', [$path])
  348. );
  349. }
  350. }