Completed
Push — stable8.2 ( a5de6b...cd78b9 )
by Lukas
30s
created

EncryptionTest::testUpdateMissingPublicKey()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1.1662

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 32
ccs 9
cts 20
cp 0.45
rs 8.8571
c 1
b 0
f 0
cc 1
eloc 20
nc 1
nop 0
crap 1.1662
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2015, ownCloud, Inc.
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace OCA\Encryption\Tests\lib\Crypto;
24
25
use OCA\Encryption\Exceptions\PublicKeyMissingException;
26
use Test\TestCase;
27
use OCA\Encryption\Crypto\Encryption;
28
29
class EncryptionTest extends TestCase {
30
31
	/** @var Encryption */
32
	private $instance;
33
34
	/** @var \PHPUnit_Framework_MockObject_MockObject */
35
	private $keyManagerMock;
36
37
	/** @var \PHPUnit_Framework_MockObject_MockObject */
38
	private $encryptAllMock;
39
40
	/** @var \PHPUnit_Framework_MockObject_MockObject */
41
	private $decryptAllMock;
42
43
	/** @var \PHPUnit_Framework_MockObject_MockObject */
44
	private $sessionMock;
45
46
	/** @var \PHPUnit_Framework_MockObject_MockObject */
47
	private $cryptMock;
48
49
	/** @var \PHPUnit_Framework_MockObject_MockObject */
50
	private $utilMock;
51
52
	/** @var \PHPUnit_Framework_MockObject_MockObject */
53
	private $loggerMock;
54
55
	/** @var \PHPUnit_Framework_MockObject_MockObject */
56
	private $l10nMock;
57
58 23
	public function setUp() {
59 23
		parent::setUp();
60
61 23
		$this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
62 23
			->disableOriginalConstructor()
63 23
			->getMock();
64 23
		$this->utilMock = $this->getMockBuilder('OCA\Encryption\Util')
65 23
			->disableOriginalConstructor()
66 23
			->getMock();
67 23
		$this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')
68 23
			->disableOriginalConstructor()
69 23
			->getMock();
70 23
		$this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session')
71 23
			->disableOriginalConstructor()
72 23
			->getMock();
73 23
		$this->encryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\EncryptAll')
74 23
			->disableOriginalConstructor()
75 23
			->getMock();
76 23
		$this->decryptAllMock = $this->getMockBuilder('OCA\Encryption\Crypto\DecryptAll')
77 23
			->disableOriginalConstructor()
78 23
			->getMock();
79 23
		$this->loggerMock = $this->getMockBuilder('OCP\ILogger')
80 23
			->disableOriginalConstructor()
81 23
			->getMock();
82 23
		$this->l10nMock = $this->getMockBuilder('OCP\IL10N')
83 23
			->disableOriginalConstructor()
84 23
			->getMock();
85 23
		$this->l10nMock->expects($this->any())
86 23
			->method('t')
87 23
			->with($this->anything())
88 23
			->willReturnArgument(0);
89
90 23
		$this->instance = new Encryption(
91 23
			$this->cryptMock,
92 23
			$this->keyManagerMock,
93 23
			$this->utilMock,
94 23
			$this->sessionMock,
95 23
			$this->encryptAllMock,
96 23
			$this->decryptAllMock,
97 23
			$this->loggerMock,
98 23
			$this->l10nMock
99 23
		);
100
101 23
	}
102
103
	/**
104
	 * test if public key from one of the recipients is missing
105
	 */
106 1
	public function testEndUser1() {
107 1
		$this->instance->begin('/foo/bar', 'user1', 'r', array(), array('users' => array('user1', 'user2', 'user3')));
108 1
		$this->endTest();
109 1
	}
110
111
	/**
112
	 * test if public key from owner is missing
113
	 *
114
	 * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException
115
	 */
116 1
	public function testEndUser2() {
117 1
		$this->instance->begin('/foo/bar', 'user2', 'r', array(), array('users' => array('user1', 'user2', 'user3')));
118 1
		$this->endTest();
119
	}
120
121
	/**
122
	 * common part of testEndUser1 and testEndUser2
123
	 *
124
	 * @throws PublicKeyMissingException
125
	 */
126 2
	public function endTest() {
127
		// prepare internal variables
128 2
		self::invokePrivate($this->instance, 'isWriteOperation', [true]);
129 2
		self::invokePrivate($this->instance, 'writeCache', ['']);
130
131 2
		$this->keyManagerMock->expects($this->any())
132 2
			->method('getPublicKey')
133 2
			->will($this->returnCallback([$this, 'getPublicKeyCallback']));
134 2
		$this->keyManagerMock->expects($this->any())
135 2
			->method('addSystemKeys')
136 2
			->will($this->returnCallback([$this, 'addSystemKeysCallback']));
137 2
		$this->cryptMock->expects($this->any())
138 2
			->method('multiKeyEncrypt')
139 2
			->willReturn(true);
140 2
		$this->cryptMock->expects($this->any())
141 2
			->method('setAllFileKeys')
142 2
			->willReturn(true);
143
144 2
		$this->instance->end('/foo/bar');
145 1
	}
146
147
148 2
	public function getPublicKeyCallback($uid) {
149 2
		if ($uid === 'user2') {
150 2
			throw new PublicKeyMissingException($uid);
151
		}
152 2
		return $uid;
153
	}
154
155 1
	public function addSystemKeysCallback($accessList, $publicKeys) {
156 1
		$this->assertSame(2, count($publicKeys));
157 1
		$this->assertArrayHasKey('user1', $publicKeys);
158 1
		$this->assertArrayHasKey('user3', $publicKeys);
159 1
		return $publicKeys;
160
	}
161
162
	/**
163
	 * @dataProvider dataProviderForTestGetPathToRealFile
164
	 */
165 4
	public function testGetPathToRealFile($path, $expected) {
166 4
		$this->assertSame($expected,
167 4
			self::invokePrivate($this->instance, 'getPathToRealFile', array($path))
168 4
		);
169 4
	}
170
171 View Code Duplication
	public function dataProviderForTestGetPathToRealFile() {
172
		return array(
173
			array('/user/files/foo/bar.txt', '/user/files/foo/bar.txt'),
174
			array('/user/files/foo.txt', '/user/files/foo.txt'),
175
			array('/user/files_versions/foo.txt.v543534', '/user/files/foo.txt'),
176
			array('/user/files_versions/foo/bar.txt.v5454', '/user/files/foo/bar.txt'),
177
		);
178
	}
179
180
	/**
181
	 * @dataProvider dataTestBegin
182
	 */
183 4
	public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected) {
184
185 4
		$this->sessionMock->expects($this->once())
186 4
			->method('decryptAllModeActivated')
187 4
			->willReturn(false);
188
189 4
		$this->sessionMock->expects($this->never())->method('getDecryptAllUid');
190 4
		$this->sessionMock->expects($this->never())->method('getDecryptAllKey');
191 4
		$this->keyManagerMock->expects($this->never())->method('getEncryptedFileKey');
192 4
		$this->keyManagerMock->expects($this->never())->method('getShareKey');
193 4
		$this->cryptMock->expects($this->never())->method('multiKeyDecrypt');
194
195 4
		$this->cryptMock->expects($this->any())
196 4
			->method('getCipher')
197 4
			->willReturn($defaultCipher);
198 4
		$this->cryptMock->expects($this->any())
199 4
			->method('getLegacyCipher')
200 4
			->willReturn($legacyCipher);
201 4
		if (empty($fileKey)) {
202 1
			$this->cryptMock->expects($this->once())
203 1
				->method('generateFileKey')
204 1
				->willReturn('fileKey');
205 1
		} else {
206 3
			$this->cryptMock->expects($this->never())
207 3
				->method('generateFileKey');
208
		}
209
210 4
		$this->keyManagerMock->expects($this->once())
211 4
			->method('getFileKey')
212 4
			->willReturn($fileKey);
213
214 4
		$result = $this->instance->begin('/user/files/foo.txt', 'user', $mode, $header, []);
215
216 4
		$this->assertArrayHasKey('cipher', $result);
217 4
		$this->assertSame($expected, $result['cipher']);
218 4
		if ($mode === 'w') {
219 2
			$this->assertTrue(self::invokePrivate($this->instance, 'isWriteOperation'));
220 2
		} else {
221 2
			$this->assertFalse(self::invokePrivate($this->instance, 'isWriteOperation'));
222
		}
223 4
	}
224
225
	public function dataTestBegin() {
226
		return array(
227
			array('w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'),
228
			array('r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'),
229
			array('w', [], 'legacyCipher', 'defaultCipher', '', 'defaultCipher'),
230
			array('r', [], 'legacyCipher', 'defaultCipher', 'file_key', 'legacyCipher'),
231
		);
232
	}
233
234
235
	/**
236
	 * test begin() if decryptAll mode was activated
237
	 */
238 1
	public function testBeginDecryptAll() {
239
240 1
		$path = '/user/files/foo.txt';
241 1
		$recoveryKeyId = 'recoveryKeyId';
242 1
		$recoveryShareKey = 'recoveryShareKey';
243 1
		$decryptAllKey = 'decryptAllKey';
244 1
		$fileKey = 'fileKey';
245
246 1
		$this->sessionMock->expects($this->once())
247 1
			->method('decryptAllModeActivated')
248 1
			->willReturn(true);
249 1
		$this->sessionMock->expects($this->once())
250 1
			->method('getDecryptAllUid')
251 1
			->willReturn($recoveryKeyId);
252 1
		$this->sessionMock->expects($this->once())
253 1
			->method('getDecryptAllKey')
254 1
			->willReturn($decryptAllKey);
255
256 1
		$this->keyManagerMock->expects($this->once())
257 1
			->method('getEncryptedFileKey')
258 1
			->willReturn('encryptedFileKey');
259 1
		$this->keyManagerMock->expects($this->once())
260 1
			->method('getShareKey')
261 1
			->with($path, $recoveryKeyId)
262 1
			->willReturn($recoveryShareKey);
263 1
		$this->cryptMock->expects($this->once())
264 1
			->method('multiKeyDecrypt')
265 1
			->with('encryptedFileKey', $recoveryShareKey, $decryptAllKey)
266 1
			->willReturn($fileKey);
267
268 1
		$this->keyManagerMock->expects($this->never())->method('getFileKey');
269
270 1
		$this->instance->begin($path, 'user', 'r', [], []);
271
272 1
		$this->assertSame($fileKey,
273 1
			$this->invokePrivate($this->instance, 'fileKey')
274 1
		);
275 1
	}
276
277
	/**
278
	 * @dataProvider dataTestUpdate
279
	 *
280
	 * @param string $fileKey
281
	 * @param boolean $expected
282
	 */
283 2
	public function testUpdate($fileKey, $expected) {
284 2
		$this->keyManagerMock->expects($this->once())
285 2
			->method('getFileKey')->willReturn($fileKey);
286
287 2
		$this->keyManagerMock->expects($this->any())
288 2
			->method('getPublicKey')->willReturn('publicKey');
289
290 2
		$this->keyManagerMock->expects($this->any())
291 2
			->method('addSystemKeys')
292 2
			->willReturnCallback(function($accessList, $publicKeys) {
293 1
				return $publicKeys;
294 2
			});
295
296 2
		$this->assertSame($expected,
297 2
			$this->instance->update('path', 'user1', ['users' => ['user1']])
298 2
		);
299
	}
300 2
301
	public function dataTestUpdate() {
302
		return array(
303
			array('', false),
304
			array('fileKey', true)
305
		);
306
	}
307
308
	/**
309
	 * Test case if the public key is missing. ownCloud should still encrypt
310
	 * the file for the remaining users
311
	 */
312
	public function testUpdateMissingPublicKey() {
313
		$this->keyManagerMock->expects($this->once())
314
			->method('getFileKey')->willReturn('fileKey');
315 8
316 8
		$this->keyManagerMock->expects($this->any())
317 8
			->method('getPublicKey')->willReturnCallback(
318 8
				function($user) {
319 8
					throw new PublicKeyMissingException($user);
320
				}
321
			);
322
323
		$this->keyManagerMock->expects($this->any())
324
			->method('addSystemKeys')
325
			->willReturnCallback(function($accessList, $publicKeys) {
326
				return $publicKeys;
327
			});
328
329
		$this->cryptMock->expects($this->once())->method('multiKeyEncrypt')
330
			->willReturnCallback(
331
				function($fileKey, $publicKeys) {
332
					$this->assertEmpty($publicKeys);
333
					$this->assertSame('fileKey', $fileKey);
334
				}
335
			);
336
337
		$this->keyManagerMock->expects($this->never())->method('getVersion');
338 1
		$this->keyManagerMock->expects($this->never())->method('setVersion');
339 1
340
		$this->assertTrue(
341
			$this->instance->update('path', 'user1', ['users' => ['user1']])
342 1
		);
343 1
	}
344 1
345
	/**
346 1
	 * by default the encryption module should encrypt regular files, files in
347 1
	 * files_versions and files in files_trashbin
348
	 *
349 1
	 * @dataProvider dataTestShouldEncrypt
350 1
	 */
351
	public function testShouldEncrypt($path, $expected) {
352
		$this->assertSame($expected,
353
			$this->instance->shouldEncrypt($path)
354
		);
355
	}
356
357 View Code Duplication
	public function dataTestShouldEncrypt() {
358
		return array(
359
			array('/user1/files/foo.txt', true),
360
			array('/user1/files_versions/foo.txt', true),
361
			array('/user1/files_trashbin/foo.txt', true),
362
			array('/user1/some_folder/foo.txt', false),
363
			array('/user1/foo.txt', false),
364
			array('/user1/files', false),
365
			array('/user1/files_trashbin', false),
366
			array('/user1/files_versions', false),
367
		);
368
	}
369
370
	/**
371
	 * @expectedException \OC\Encryption\Exceptions\DecryptionFailedException
372
	 * @expectedExceptionMessage Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.
373
	 */
374
	public function testDecrypt() {
375
		$this->instance->decrypt('abc');
376
	}
377
378
	public function testPrepareDecryptAll() {
379
		$input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
380
		$output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
381
382
		$this->decryptAllMock->expects($this->once())->method('prepare')
383
			->with($input, $output, 'user');
384
385
		$this->instance->prepareDecryptAll($input, $output, 'user');
386
	}
387
388
}
389