Completed
Push — master ( 24957e...d8c031 )
by Thomas
16:29 queued 07:32
created

DecryptAll   B

Complexity

Total Complexity 33

Size/Duplication

Total Lines 274
Duplicated Lines 8.76 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
dl 24
loc 274
rs 8.6166
c 0
b 0
f 0
wmc 33
lcom 1
cbo 15

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
C decryptAll() 5 33 7
A prepareEncryptionModules() 0 17 3
B decryptAllUsersFiles() 19 58 8
D decryptUsersFiles() 0 45 9
A decryptFile() 0 19 3
A getTimestamp() 0 3 1
A setupUserFS() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Christian Jürges <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 * @author Vincent Petry <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2017, ownCloud GmbH
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
27
namespace OC\Encryption;
28
29
use OC\Encryption\Exceptions\DecryptionFailedException;
30
use OC\Files\View;
31
use \OCP\Encryption\IEncryptionModule;
32
use OCP\ILogger;
33
use OCP\IUserManager;
34
use Symfony\Component\Console\Helper\ProgressBar;
35
use Symfony\Component\Console\Input\InputInterface;
36
use Symfony\Component\Console\Output\OutputInterface;
37
38
class DecryptAll {
39
40
	/** @var  OutputInterface */
41
	protected $output;
42
43
	/** @var  InputInterface */
44
	protected $input;
45
46
	/** @var  Manager */
47
	protected $encryptionManager;
48
49
	/** @var IUserManager */
50
	protected $userManager;
51
52
	/** @var View */
53
	protected $rootView;
54
55
	/** @var  array files which couldn't be decrypted */
56
	protected $failed;
57
58
	/** @var ILogger */
59
	protected $logger;
60
61
	/**
62
	 * @param Manager $encryptionManager
63
	 * @param IUserManager $userManager
64
	 * @param View $rootView
65
	 * @param ILogger $logger
66
	 */
67
	public function __construct(
68
		Manager $encryptionManager,
69
		IUserManager $userManager,
70
		View $rootView,
71
		ILogger $logger
72
	) {
73
		$this->encryptionManager = $encryptionManager;
74
		$this->userManager = $userManager;
75
		$this->rootView = $rootView;
76
		$this->failed = [];
77
		$this->logger = $logger;
78
	}
79
80
	/**
81
	 * start to decrypt all files
82
	 *
83
	 * @param InputInterface $input
84
	 * @param OutputInterface $output
85
	 * @param string $user which users data folder should be decrypted, default = all users
86
	 * @return bool
87
	 * @throws \Exception
88
	 */
89
	public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') {
90
91
		$this->input = $input;
92
		$this->output = $output;
93
94
		if ($user !== '' && $this->userManager->userExists($user) === false) {
95
			$this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again');
96
			return false;
97
		}
98
99
		$this->output->writeln('prepare encryption modules...');
100 View Code Duplication
		if (\OC::$server->getAppConfig()->getValue('encryption', 'useMasterKey', '0') !== '0') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
			if ($this->prepareEncryptionModules($user) === false) {
102
				return false;
103
			}
104
		}
105
		$this->output->writeln(' done.');
106
107
		$this->decryptAllUsersFiles($user);
108
109
		if (empty($this->failed)) {
110
			$this->output->writeln('all files could be decrypted successfully!');
111
		} else {
112
			$this->output->writeln('Files for following users couldn\'t be decrypted, ');
113
			$this->output->writeln('maybe the user is not set up in a way that supports this operation: ');
114
			foreach ($this->failed as $uid => $paths) {
115
				$this->output->writeln('    ' . $uid);
116
			}
117
			$this->output->writeln('');
118
		}
119
120
		return true;
121
	}
122
123
	/**
124
	 * prepare encryption modules to perform the decrypt all function
125
	 *
126
	 * @param $user
127
	 * @return bool
128
	 */
129
	protected function prepareEncryptionModules($user) {
130
		// prepare all encryption modules for decrypt all
131
		$encryptionModules = $this->encryptionManager->getEncryptionModules();
132
		foreach ($encryptionModules as $moduleDesc) {
133
			/** @var IEncryptionModule $module */
134
			$module = call_user_func($moduleDesc['callback']);
135
			$this->output->writeln('');
136
			$this->output->writeln('Prepare "' . $module->getDisplayName() . '"');
137
			$this->output->writeln('');
138
			if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) {
139
				$this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!');
140
				return false;
141
			}
142
		}
143
144
		return true;
145
	}
146
147
	/**
148
	 * iterate over all user and encrypt their files
149
	 *
150
	 * @param string $user which users files should be decrypted, default = all users
151
	 * @return bool
152
	 */
153
	protected function decryptAllUsersFiles($user = '') {
154
155
		$this->output->writeln("\n");
156
157
		$userList = [];
158
		if ($user === '') {
159
160
			$fetchUsersProgress = new ProgressBar($this->output);
161
			$fetchUsersProgress->setFormat(" %message% \n [%bar%]");
162
			$fetchUsersProgress->start();
163
			$fetchUsersProgress->setMessage("Fetch list of users...");
164
			$fetchUsersProgress->advance();
165
166 View Code Duplication
			foreach ($this->userManager->getBackends() as $backend) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
				$limit = 500;
168
				$offset = 0;
169
				do {
170
					$users = $backend->getUsers('', $limit, $offset);
171
					foreach ($users as $user) {
172
						$userList[] = $user;
173
					}
174
					$offset += $limit;
175
					$fetchUsersProgress->advance();
176
				} while (count($users) >= $limit);
177
				$fetchUsersProgress->setMessage("Fetch list of users... finished");
178
				$fetchUsersProgress->finish();
179
			}
180
		} else {
181
			$userList[] = $user;
182
		}
183
184
		$this->output->writeln("\n\n");
185
186
		$progress = new ProgressBar($this->output);
187
		$progress->setFormat(" %message% \n [%bar%]");
188
		$progress->start();
189
		$progress->setMessage("starting to decrypt files...");
190
		$progress->advance();
191
192
		$numberOfUsers = count($userList);
193
		$userNo = 1;
194
		foreach ($userList as $uid) {
195
			$userCount = "$uid ($userNo of $numberOfUsers)";
196 View Code Duplication
			if (\OC::$server->getAppConfig()->getValue('encryption', 'userSpecificKey', '0') !== '0') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
				if ($this->prepareEncryptionModules($uid) === false) {
198
					return false;
199
				}
200
			}
201
			$this->decryptUsersFiles($uid, $progress, $userCount);
202
			$userNo++;
203
		}
204
205
		$progress->setMessage("starting to decrypt files... finished");
206
		$progress->finish();
207
208
		$this->output->writeln("\n\n");
209
		return true;
210
	}
211
212
	/**
213
	 * encrypt files from the given user
214
	 *
215
	 * @param string $uid
216
	 * @param ProgressBar $progress
217
	 * @param string $userCount
218
	 */
219
	protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) {
220
221
		$this->setupUserFS($uid);
222
		$directories = [];
223
		$directories[] = '/' . $uid . '/files';
224
225
		while ($root = array_pop($directories)) {
226
			$content = $this->rootView->getDirectoryContent($root);
227
			foreach ($content as $file) {
228
				// only decrypt files owned by the user
229
				if($file->getStorage()->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
230
						continue;
231
				}
232
				$path = $root . '/' . $file['name'];
233
				if ($this->rootView->is_dir($path)) {
234
					$directories[] = $path;
235
					continue;
236
				} else {
237
					try {
238
						$progress->setMessage("decrypt files for user $userCount: $path");
239
						$progress->advance();
240
						if ($file->isEncrypted() === false) {
241
							$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
242
							$progress->advance();
243
						} else {
244
							if ($this->decryptFile($path) === false) {
245
								$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
246
								$progress->advance();
247
							}
248
						}
249
					} catch (\Exception $e) {
250
						$this->logger->logException($e, [
251
							'message' => "Exception trying to decrypt file <$path> for user <$uid>",
252
							'app' => __CLASS__
253
						]);
254
						if (isset($this->failed[$uid])) {
255
							$this->failed[$uid][] = $path;
256
						} else {
257
							$this->failed[$uid] = [$path];
258
						}
259
					}
260
				}
261
			}
262
		}
263
	}
264
265
	/**
266
	 * encrypt file
267
	 *
268
	 * @param string $path
269
	 * @return bool
270
	 */
271
	protected function decryptFile($path) {
272
273
		$source = $path;
274
		$target = $path . '.decrypted.' . $this->getTimestamp();
275
276
		try {
277
			\OC\Files\Storage\Wrapper\Encryption::setDisableWriteEncryption(true);
278
			$this->rootView->copy($source, $target);
279
			\OC\Files\Storage\Wrapper\Encryption::setDisableWriteEncryption(false);
280
			$this->rootView->rename($target, $source);
281
		} catch (DecryptionFailedException $e) {
282
			if ($this->rootView->file_exists($target)) {
283
				$this->rootView->unlink($target);
284
			}
285
			return false;
286
		}
287
288
		return true;
289
	}
290
291
	/**
292
	 * get current timestamp
293
	 *
294
	 * @return int
295
	 */
296
	protected function getTimestamp() {
297
		return time();
298
	}
299
300
301
	/**
302
	 * setup user file system
303
	 *
304
	 * @param string $uid
305
	 */
306
	protected function setupUserFS($uid) {
307
		\OC_Util::tearDownFS();
308
		\OC_Util::setupFS($uid);
309
	}
310
311
}
312