aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_encryption/tests/util.php
blob: 4174a0da0da1f04d11afd6643e1f0243f5ba5f43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<?php
/**
 * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
 * This file is licensed under the Affero General Public License version 3 or
 * later.
 * See the COPYING-README file.
 */

/**
 * logging utilities
 *
 * Log is saved by default at data/owncloud.log using OC_Log_Owncloud.
 * Selecting other backend is done with a config option 'log_type'.
 */

class OC_Log {
	const DEBUG=0;
	const INFO=1;
	const WARN=2;
	const ERROR=3;
	const FATAL=4;

	static public $enabled = true;
	static protected $class = null;

	/**
	 * write a message in the log
	 * @param string $app
	 * @param string $message
	 * @param int level
	 */
	public static function write($app, $message, $level) {
		if (self::$enabled) {
			if (!self::$class) {
				self::$class = 'OC_Log_'.ucfirst(OC_Config::getValue('log_type', 'owncloud'));
				call_user_func(array(self::$class, 'init'));
			}
			$log_class=self::$class;
			$log_class::write($app, $message, $level);
		}
	}
	
	//Fatal errors handler
	public static function onShutdown() {
		$error = error_get_last();
		if($error) {
			//ob_end_clean();
			self::write('PHP', $error['message'] . ' at ' . $error['file'] . '#' . $error['line'], self::FATAL);
		} else {
			return true;
		}
	}
	
	// Uncaught exception handler
	public static function onException($exception) {
		self::write('PHP', $exception->getMessage() . ' at ' . $exception->getFile() . '#' . $exception->getLine(), self::FATAL);
	}

	//Recoverable errors handler
	public static function onError($number, $message, $file, $line) {
		if (error_reporting() === 0) {
			return;
		}
		self::write('PHP', $message . ' at ' . $file . '#' . $line, self::WARN);

	}
}
'n454' href='#n454'>454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
<?php
/**
 * @author Andreas Fischer <bantu@owncloud.com>
 * @author Björn Schießle <schiessle@owncloud.com>
 * @author Florin Peter <github@florin-peter.de>
 * @author Joas Schilling <nickvergessen@owncloud.com>
 * @author Jörn Friedrich Dreyer <jfd@butonic.de>
 * @author Markus Goetz <markus@woboq.com>
 * @author Morris Jobke <hey@morrisjobke.de>
 * @author Robin Appelman <icewind@owncloud.com>
 * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
 * @author Sam Tuke <mail@samtuke.com>
 * @author Thomas Müller <thomas.mueller@tmit.eu>
 * @author Vincent Petry <pvince81@owncloud.com>
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 * @license AGPL-3.0
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

namespace OCA\Files_Encryption\Tests;

/**
 * Class Util
 */
class Util extends TestCase {

	const TEST_ENCRYPTION_UTIL_USER1 = "test-util-user1";
	const TEST_ENCRYPTION_UTIL_USER2 = "test-util-user2";
	const TEST_ENCRYPTION_UTIL_GROUP1 = "test-util-group1";
	const TEST_ENCRYPTION_UTIL_GROUP2 = "test-util-group2";
	const TEST_ENCRYPTION_UTIL_LEGACY_USER = "test-legacy-user";

	public $userId;
	public $encryptionDir;
	public $publicKeyDir;
	public $pass;
	/**
	 * @var \OC\Files\View
	 */
	public $view;
	public $keysPath;
	public $publicKeyPath;
	public $privateKeyPath;
	/**
	 * @var \OCA\Files_Encryption\Util
	 */
	public $util;
	public $dataShort;
	public $legacyEncryptedData;
	public $legacyEncryptedDataKey;
	public $legacyKey;
	public $stateFilesTrashbin;

	public static function setUpBeforeClass() {
		parent::setUpBeforeClass();

		// create test user
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1, true);
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER2, true);
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_LEGACY_USER, true);

		// create groups
		\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
		\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);

		// add user 1 to group1
		\OC_Group::addToGroup(self::TEST_ENCRYPTION_UTIL_USER1, self::TEST_ENCRYPTION_UTIL_GROUP1);
	}

	protected function setUp() {
		parent::setUp();

		// login user
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
		\OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);
		$this->userId = self::TEST_ENCRYPTION_UTIL_USER1;
		$this->pass = self::TEST_ENCRYPTION_UTIL_USER1;

		// set content for encrypting / decrypting in tests
		$this->dataUrl = __DIR__ . '/../lib/crypt.php';
		$this->dataShort = 'hats';
		$this->dataLong = file_get_contents(__DIR__ . '/../lib/crypt.php');
		$this->legacyData = __DIR__ . '/legacy-text.txt';
		$this->legacyEncryptedData = __DIR__ . '/legacy-encrypted-text.txt';
		$this->legacyEncryptedDataKey = __DIR__ . '/encryption.key';
		$this->legacyKey = "30943623843030686906\0\0\0\0";

		$keypair = \OCA\Files_Encryption\Crypt::createKeypair();

		$this->genPublicKey = $keypair['publicKey'];
		$this->genPrivateKey = $keypair['privateKey'];

		$this->publicKeyDir = \OCA\Files_Encryption\Keymanager::getPublicKeyPath();
		$this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
		$this->keysPath = $this->encryptionDir . '/' . 'keys';
		$this->publicKeyPath =
			$this->publicKeyDir . '/' . $this->userId . '.publicKey'; // e.g. data/public-keys/admin.publicKey
		$this->privateKeyPath =
			$this->encryptionDir . '/' . $this->userId . '.privateKey'; // e.g. data/admin/admin.privateKey

		$this->view = new \OC\Files\View('/');

		$this->util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		// remember files_trashbin state
		$this->stateFilesTrashbin = \OC_App::isEnabled('files_trashbin');

		// we don't want to tests with app files_trashbin enabled
		\OC_App::disable('files_trashbin');
	}

	protected function tearDown() {
		// reset app files_trashbin
		if ($this->stateFilesTrashbin) {
			\OC_App::enable('files_trashbin');
		}
		else {
			\OC_App::disable('files_trashbin');
		}

		parent::tearDown();
	}

	public static function tearDownAfterClass() {
		// cleanup test user
		\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER1);
		\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER2);
		\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_LEGACY_USER);

		//cleanup groups
		\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
		\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);

		parent::tearDownAfterClass();
	}

	/**
	 * @medium
	 * test that paths set during User construction are correct
	 */
	function testKeyPaths() {
		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		$this->assertEquals($this->publicKeyDir, $util->getPath('publicKeyDir'));
		$this->assertEquals($this->encryptionDir, $util->getPath('encryptionDir'));
		$this->assertEquals($this->keysPath, $util->getPath('keysPath'));
		$this->assertEquals($this->publicKeyPath, $util->getPath('publicKeyPath'));
		$this->assertEquals($this->privateKeyPath, $util->getPath('privateKeyPath'));

	}

	/**
	 * @medium
	 * test detection of encrypted files
	 */
	function testIsEncryptedPath() {

		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		self::loginHelper($this->userId);

		$unencryptedFile = '/tmpUnencrypted-' . $this->getUniqueID() . '.txt';
		$encryptedFile =  '/tmpEncrypted-' . $this->getUniqueID() . '.txt';

		// Disable encryption proxy to write a unencrypted file
		$proxyStatus = \OC_FileProxy::$enabled;
		\OC_FileProxy::$enabled = false;

		$this->view->file_put_contents($this->userId . '/files/' . $unencryptedFile, $this->dataShort);

		// Re-enable proxy - our work is done
		\OC_FileProxy::$enabled = $proxyStatus;

		// write a encrypted file
		$this->view->file_put_contents($this->userId . '/files/' . $encryptedFile, $this->dataShort);

		// test if both files are detected correctly
		$this->assertFalse($util->isEncryptedPath($this->userId . '/files/' . $unencryptedFile));
		$this->assertTrue($util->isEncryptedPath($this->userId . '/files/' . $encryptedFile));

		// cleanup
		$this->view->unlink($this->userId . '/files/' . $unencryptedFile);
		$this->view->unlink($this->userId . '/files/' . $encryptedFile);

	}

	/**
	 * @medium
	 * test setup of encryption directories
	 */
	function testSetupServerSide() {
		$this->assertEquals(true, $this->util->setupServerSide($this->pass));
	}

	/**
	 * @medium
	 * test checking whether account is ready for encryption,
	 */
	function testUserIsReady() {
		$this->assertEquals(true, $this->util->ready());
	}

	/**
	 * test checking whether account is not ready for encryption,
	 */
//	function testUserIsNotReady() {
//		$this->view->unlink($this->publicKeyDir);
//
//		$params['uid'] = $this->userId;
//		$params['password'] = $this->pass;
//		$this->assertFalse(OCA\Files_Encryption\Hooks::login($params));
//
//		$this->view->unlink($this->privateKeyPath);
//	}

	/**
	 * @medium
	 */
	function testRecoveryEnabledForUser() {

		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		// Record the value so we can return it to it's original state later
		$enabled = $util->recoveryEnabledForUser();

		$this->assertTrue($util->setRecoveryForUser(!$enabled));

		$this->assertEquals(!$enabled, $util->recoveryEnabledForUser());

		$this->assertTrue($util->setRecoveryForUser($enabled));

		$this->assertEquals($enabled, $util->recoveryEnabledForUser());


	}

	/**
	 * @medium
	 */
	function testGetUidAndFilename() {

		\OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);

		$filename = '/tmp-' . $this->getUniqueID() . '.test';

		// Disable encryption proxy to prevent recursive calls
		$proxyStatus = \OC_FileProxy::$enabled;
		\OC_FileProxy::$enabled = false;

		$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);

		// Re-enable proxy - our work is done
		\OC_FileProxy::$enabled = $proxyStatus;

		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		list($fileOwnerUid, $file) = $util->getUidAndFilename($filename);

		$this->assertEquals(self::TEST_ENCRYPTION_UTIL_USER1, $fileOwnerUid);

		$this->assertEquals($file, $filename);

		$this->view->unlink($this->userId . '/files/' . $filename);
	}

	/**
	 * Test that data that is read by the crypto stream wrapper
	 */
	function testGetFileSize() {
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);

		$filename = 'tmp-' . $this->getUniqueID();
		$externalFilename = '/' . $this->userId . '/files/' . $filename;

		// Test for 0 byte files
		$problematicFileSizeData = "";
		$cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
		$this->assertTrue(is_int($cryptedFile));
		$this->assertEquals($this->util->getFileSize($externalFilename), 0);
		$decrypt = $this->view->file_get_contents($externalFilename);
		$this->assertEquals($problematicFileSizeData, $decrypt);
		$this->view->unlink($this->userId . '/files/' . $filename);

		// Test a file with 18377 bytes as in https://github.com/owncloud/mirall/issues/1009
		$problematicFileSizeData = str_pad("", 18377, "abc");
		$cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
		$this->assertTrue(is_int($cryptedFile));
		$this->assertEquals($this->util->getFileSize($externalFilename), 18377);
		$decrypt = $this->view->file_get_contents($externalFilename);
		$this->assertEquals($problematicFileSizeData, $decrypt);
		$this->view->unlink($this->userId . '/files/' . $filename);
	}

	function testEncryptAll() {

		$filename = "/encryptAll" . $this->getUniqueID() . ".txt";
		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		// disable encryption to upload a unencrypted file
		\OC_App::disable('files_encryption');

		$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);

		$fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);

		$this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo);

		// enable file encryption again
		\OC_App::enable('files_encryption');

		// encrypt all unencrypted files
		$util->encryptAll('/' . $this->userId . '/' . 'files');

		$fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);

		$this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo);

		// check if mtime and etags unchanged
		$this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']);
		$this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']);

		$this->view->unlink($this->userId . '/files/' . $filename);
	}

	function testDecryptAll() {

		$filename = "/decryptAll" . $this->getUniqueID() . ".txt";
		$datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/');
		$userdir = $datadir . '/' . $this->userId . '/files/';

		$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);

		$fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);

		$this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo);
		$this->assertEquals($fileInfoEncrypted['encrypted'], 1);

		$encContent = file_get_contents($userdir . $filename);

		\OC_App::disable('files_encryption');

		$user = \OCP\User::getUser();
		$this->logoutHelper();
		$this->loginHelper($user, false, false, false);

		$content = file_get_contents($userdir . $filename);

		//content should be encrypted
		$this->assertSame($encContent, $content);

		// now we load the encryption app again
		\OC_App::loadApp('files_encryption');

		// init encryption app
		$params = array('uid' => \OCP\User::getUser(),
			'password' => \OCP\User::getUser());

		$view = new \OC\Files\View('/');
		$util = new \OCA\Files_Encryption\Util($view, \OCP\User::getUser());

		$result = $util->initEncryption($params);

		$this->assertTrue($result instanceof \OCA\Files_Encryption\Session);

		$successful = $util->decryptAll();

		$this->assertTrue($successful);

		$this->logoutHelper();
		$this->loginHelper($user, false, false, false);

		// file should be unencrypted and fileInfo should contain the correct values
		$content = file_get_contents($userdir . $filename);

		// now we should get the plain data
		$this->assertSame($this->dataShort, $content);

		$fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
		$this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo);

		// check if mtime and etags unchanged
		$this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']);
		$this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']);
		// file should no longer be encrypted
		$this->assertEquals(0, $fileInfoUnencrypted['encrypted']);

		$backupPath = $this->getBackupPath('decryptAll');

		// check if the keys where moved to the backup location
		$this->assertTrue($this->view->is_dir($backupPath . '/keys'));
		$this->assertTrue($this->view->file_exists($backupPath . '/keys/' . $filename . '/fileKey'));
		$this->assertTrue($this->view->file_exists($backupPath . '/keys/' . $filename . '/' . $user . '.shareKey'));

		// cleanup
		$this->view->unlink($this->userId . '/files/' . $filename);
		$this->view->deleteAll($backupPath);
		\OC_App::enable('files_encryption');

	}

	private function createDummyKeysForBackupTest() {
		// create some dummy key files
		$encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
		$this->view->mkdir($encPath . '/keys/foo');
		$this->view->file_put_contents($encPath . '/keys/foo/fileKey', 'key');
		$this->view->file_put_contents($encPath . '/keys/foo/user1.shareKey', 'share key');
	}

	/**
	 * test if all keys get moved to the backup folder correctly
	 *
	 * @dataProvider dataBackupAllKeys
	 */
	function testBackupAllKeys($addTimestamp, $includeUserKeys) {
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);

		$this->createDummyKeysForBackupTest();

		$util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);

		$util->backupAllKeys('testBackupAllKeys', $addTimestamp, $includeUserKeys);

		$backupPath = $this->getBackupPath('testBackupAllKeys');

		// check backupDir Content
		$this->assertTrue($this->view->is_dir($backupPath . '/keys'));
		$this->assertTrue($this->view->is_dir($backupPath . '/keys/foo'));
		$this->assertTrue($this->view->file_exists($backupPath . '/keys/foo/fileKey'));
		$this->assertTrue($this->view->file_exists($backupPath . '/keys/foo/user1.shareKey'));

		if ($includeUserKeys) {
			$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.privateKey'));
			$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.publicKey'));
		} else {
			$this->assertFalse($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.privateKey'));
			$this->assertFalse($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.publicKey'));
		}

		//cleanup
		$this->view->deleteAll($backupPath);
		$this->view->unlink($this->encryptionDir . '/keys/foo/fileKey');
		$this->view->unlink($this->encryptionDir . '/keys/foo/user1.shareKey');
	}

	function dataBackupAllKeys() {
		return array(
			array(true, true),
			array(false, true),
			array(true, false),
			array(false, false),
		);
	}


	/**
	 * @dataProvider dataBackupAllKeys
	 */
	function testRestoreBackup($addTimestamp, $includeUserKeys) {

		$util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
		$this->createDummyKeysForBackupTest();

		$util->backupAllKeys('restoreKeysBackupTest', $addTimestamp, $includeUserKeys);
		$this->view->deleteAll($this->keysPath);
		if ($includeUserKeys) {
			$this->view->unlink($this->privateKeyPath);
			$this->view->unlink($this->publicKeyPath);
		}

		// key should be removed after backup was created
		$this->assertFalse($this->view->is_dir($this->keysPath));
		if ($includeUserKeys) {
			$this->assertFalse($this->view->file_exists($this->privateKeyPath));
			$this->assertFalse($this->view->file_exists($this->publicKeyPath));
		}

		$backupPath = $this->getBackupPath('restoreKeysBackupTest');
		$backupName = substr(basename($backupPath), strlen('backup.'));

		$this->assertTrue($util->restoreBackup($backupName));

		// check if all keys are restored
		$this->assertFalse($this->view->is_dir($backupPath));
		$this->assertTrue($this->view->is_dir($this->keysPath));
		$this->assertTrue($this->view->is_dir($this->keysPath . '/foo'));
		$this->assertTrue($this->view->file_exists($this->keysPath . '/foo/fileKey'));
		$this->assertTrue($this->view->file_exists($this->keysPath . '/foo/user1.shareKey'));
		$this->assertTrue($this->view->file_exists($this->privateKeyPath));
		$this->assertTrue($this->view->file_exists($this->publicKeyPath));
	}

	function testDeleteBackup() {
		$util = new \OCA\Files_Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
		$this->createDummyKeysForBackupTest();

		$util->backupAllKeys('testDeleteBackup', false, false);

		$this->assertTrue($this->view->is_dir($this->encryptionDir . '/backup.testDeleteBackup'));

		$util->deleteBackup('testDeleteBackup');

		$this->assertFalse($this->view->is_dir($this->encryptionDir . '/backup.testDeleteBackup'));
	}

	function testDescryptAllWithBrokenFiles() {

		$file1 = "/decryptAll1" . $this->getUniqueID() . ".txt";
		$file2 = "/decryptAll2" . $this->getUniqueID() . ".txt";

		$util = new \OCA\Files_Encryption\Util($this->view, $this->userId);

		$this->view->file_put_contents($this->userId . '/files/' . $file1, $this->dataShort);
		$this->view->file_put_contents($this->userId . '/files/' . $file2, $this->dataShort);

		$fileInfoEncrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
		$fileInfoEncrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);

		$this->assertTrue($fileInfoEncrypted1 instanceof \OC\Files\FileInfo);
		$this->assertTrue($fileInfoEncrypted2 instanceof \OC\Files\FileInfo);
		$this->assertEquals($fileInfoEncrypted1['encrypted'], 1);
		$this->assertEquals($fileInfoEncrypted2['encrypted'], 1);

		// rename keyfile for file1 so that the decryption for file1 fails
		// Expected behaviour: decryptAll() returns false, file2 gets decrypted anyway
		$this->view->rename($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey',
				$this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved');

		// need to reset key cache that we don't use the cached key
		$this->resetKeyCache();

		// decrypt all encrypted files
		$result = $util->decryptAll();

		$this->assertFalse($result);

		$fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
		$fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);

		$this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo);
		$this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo);

		// file1 should be still encrypted; file2 should be decrypted
		$this->assertEquals(1, $fileInfoUnencrypted1['encrypted']);
		$this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);

		// keyfiles and share keys should still exist
		$this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/keys/'));
		$this->assertTrue($this->view->file_exists($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved'));
		$this->assertTrue($this->view->file_exists($this->userId . '/files_encryption/keys/' . $file1 . '/' . $this->userId . '.shareKey'));

		// rename the keyfile for file1 back
		$this->view->rename($this->userId . '/files_encryption/keys/' . $file1 . '/fileKey.moved',
				$this->userId . '/files_encryption/keys/' . $file1 . '/fileKey');

		// try again to decrypt all encrypted files
		$result = $util->decryptAll();

		$this->assertTrue($result);

		$fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
		$fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);

		$this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo);
		$this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo);

		// now both files should be decrypted
		$this->assertEquals(0, $fileInfoUnencrypted1['encrypted']);
		$this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);

		// keyfiles and share keys should be deleted
		$this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/keys/'));

		//cleanup
		$backupPath = $this->getBackupPath('decryptAll');
		$this->view->unlink($this->userId . '/files/' . $file1);
		$this->view->unlink($this->userId . '/files/' . $file2);
		$this->view->deleteAll($backupPath);

	}

	function getBackupPath($extension) {
		$encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
		$encFolderContent = $this->view->getDirectoryContent($encPath);

		$backupPath = '';
		foreach ($encFolderContent as $c) {
			$name = $c['name'];
			if (substr($name, 0, strlen('backup.' . $extension))  === 'backup.' . $extension) {
				$backupPath = $encPath . '/'. $c['name'];
				break;
			}
		}

		return $backupPath;
	}

	/**
	 * @dataProvider dataProviderFortestIsMountPointApplicableToUser
	 */
	function testIsMountPointApplicableToUser($mount, $expectedResult) {
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
		$dummyClass = new DummyUtilClass($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
		$result = $dummyClass->testIsMountPointApplicableToUser($mount);

		$this->assertSame($expectedResult, $result);
	}

	function dataProviderFortestIsMountPointApplicableToUser() {
		return array(
			array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER1))), true),
			array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
			array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array())), true),
			array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), true),
			array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
			array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2, 'all'))), true),
			array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array('all'))), true),
		);
	}

	/**
	 * Tests that filterShareReadyUsers() returns the correct list of
	 * users that are ready or not ready for encryption
	 */
	public function testFilterShareReadyUsers() {
		$appConfig = \OC::$server->getAppConfig();

		$publicShareKeyId = $appConfig->getValue('files_encryption', 'publicShareKeyId');
		$recoveryKeyId = $appConfig->getValue('files_encryption', 'recoveryKeyId');

		$usersToTest = array(
			'readyUser',
			'notReadyUser',
			'nonExistingUser',
			$publicShareKeyId,
			$recoveryKeyId,
		);
		self::loginHelper('readyUser', true);
		self::loginHelper('notReadyUser', true);
		// delete encryption dir to make it not ready
		$this->view->unlink('notReadyUser/files_encryption/');

		// login as user1
		self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);

		$result = $this->util->filterShareReadyUsers($usersToTest);
		$this->assertEquals(
			array('readyUser', $publicShareKeyId, $recoveryKeyId),
			$result['ready']
		);
		$this->assertEquals(
			array('notReadyUser', 'nonExistingUser'),
			$result['unready']
		);
		\OC_User::deleteUser('readyUser');
	}

	/**
	 * helper function to set migration status to the right value
	 * to be able to test the migration path
	 *
	 * @param integer $status needed migration status for test
	 * @param string $user for which user the status should be set
	 * @return boolean
	 */
	private function setMigrationStatus($status, $user) {
		\OC::$server->getConfig()->setUserValue($user, 'files_encryption', 'migration_status', (string)$status);
		// the update will definitely be executed -> return value is always true
		return true;
	}

}

/**
 * dummy class extends  \OCA\Files_Encryption\Util to access protected methods for testing
 */
class DummyUtilClass extends \OCA\Files_Encryption\Util {
	public function testIsMountPointApplicableToUser($mount) {
		return $this->isMountPointApplicableToUser($mount);
	}
}