Completed
Push — stable10 ( 3ba03a...c40e59 )
by Roeland
18:21 queued 08:38
created

DecryptAll::decryptUsersFiles()   D

Complexity

Conditions 9
Paths 23

Size

Total Lines 41
Code Lines 29

Duplication

Lines 9
Ratio 21.95 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 29
c 1
b 0
f 0
nc 23
nop 3
dl 9
loc 41
rs 4.909
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Björn Schießle <[email protected]>
6
 * @author Christian Jürges <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 *
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\IUserManager;
33
use Symfony\Component\Console\Helper\ProgressBar;
34
use Symfony\Component\Console\Input\InputInterface;
35
use Symfony\Component\Console\Output\OutputInterface;
36
37
class DecryptAll {
38
39
	/** @var  OutputInterface */
40
	protected $output;
41
42
	/** @var  InputInterface */
43
	protected $input;
44
45
	/** @var  Manager */
46
	protected $encryptionManager;
47
48
	/** @var IUserManager */
49
	protected $userManager;
50
51
	/** @var View */
52
	protected $rootView;
53
54
	/** @var  array files which couldn't be decrypted */
55
	protected $failed;
56
57
	/**
58
	 * @param Manager $encryptionManager
59
	 * @param IUserManager $userManager
60
	 * @param View $rootView
61
	 */
62
	public function __construct(
63
		Manager $encryptionManager,
64
		IUserManager $userManager,
65
		View $rootView
66
	) {
67
		$this->encryptionManager = $encryptionManager;
68
		$this->userManager = $userManager;
69
		$this->rootView = $rootView;
70
		$this->failed = [];
71
	}
72
73
	/**
74
	 * start to decrypt all files
75
	 *
76
	 * @param InputInterface $input
77
	 * @param OutputInterface $output
78
	 * @param string $user which users data folder should be decrypted, default = all users
79
	 * @return bool
80
	 * @throws \Exception
81
	 */
82
	public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') {
83
84
		$this->input = $input;
85
		$this->output = $output;
86
87
		if ($user !== '' && $this->userManager->userExists($user) === false) {
88
			$this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again');
89
			return false;
90
		}
91
92
		$this->output->writeln('prepare encryption modules...');
93
		if ($this->prepareEncryptionModules($user) === false) {
94
			return false;
95
		}
96
		$this->output->writeln(' done.');
97
98
		$this->decryptAllUsersFiles($user);
99
100
		if (empty($this->failed)) {
101
			$this->output->writeln('all files could be decrypted successfully!');
102
		} else {
103
			$this->output->writeln('Files for following users couldn\'t be decrypted, ');
104
			$this->output->writeln('maybe the user is not set up in a way that supports this operation: ');
105
			foreach ($this->failed as $uid => $paths) {
106
				$this->output->writeln('    ' . $uid);
107
			}
108
			$this->output->writeln('');
109
		}
110
111
		return true;
112
	}
113
114
	/**
115
	 * prepare encryption modules to perform the decrypt all function
116
	 *
117
	 * @param $user
118
	 * @return bool
119
	 */
120
	protected function prepareEncryptionModules($user) {
121
		// prepare all encryption modules for decrypt all
122
		$encryptionModules = $this->encryptionManager->getEncryptionModules();
123
		foreach ($encryptionModules as $moduleDesc) {
124
			/** @var IEncryptionModule $module */
125
			$module = call_user_func($moduleDesc['callback']);
126
			$this->output->writeln('');
127
			$this->output->writeln('Prepare "' . $module->getDisplayName() . '"');
128
			$this->output->writeln('');
129
			if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) {
130
				$this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!');
131
				return false;
132
			}
133
		}
134
135
		return true;
136
	}
137
138
	/**
139
	 * iterate over all user and encrypt their files
140
	 *
141
	 * @param string $user which users files should be decrypted, default = all users
142
	 */
143
	protected function decryptAllUsersFiles($user = '') {
144
145
		$this->output->writeln("\n");
146
147
		$userList = [];
148
		if ($user === '') {
149
150
			$fetchUsersProgress = new ProgressBar($this->output);
151
			$fetchUsersProgress->setFormat(" %message% \n [%bar%]");
152
			$fetchUsersProgress->start();
153
			$fetchUsersProgress->setMessage("Fetch list of users...");
154
			$fetchUsersProgress->advance();
155
156 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...
157
				$limit = 500;
158
				$offset = 0;
159
				do {
160
					$users = $backend->getUsers('', $limit, $offset);
161
					foreach ($users as $user) {
162
						$userList[] = $user;
163
					}
164
					$offset += $limit;
165
					$fetchUsersProgress->advance();
166
				} while (count($users) >= $limit);
167
				$fetchUsersProgress->setMessage("Fetch list of users... finished");
168
				$fetchUsersProgress->finish();
169
			}
170
		} else {
171
			$userList[] = $user;
172
		}
173
174
		$this->output->writeln("\n\n");
175
176
		$progress = new ProgressBar($this->output);
177
		$progress->setFormat(" %message% \n [%bar%]");
178
		$progress->start();
179
		$progress->setMessage("starting to decrypt files...");
180
		$progress->advance();
181
182
		$numberOfUsers = count($userList);
183
		$userNo = 1;
184
		foreach ($userList as $uid) {
185
			$userCount = "$uid ($userNo of $numberOfUsers)";
186
			$this->decryptUsersFiles($uid, $progress, $userCount);
187
			$userNo++;
188
		}
189
190
		$progress->setMessage("starting to decrypt files... finished");
191
		$progress->finish();
192
193
		$this->output->writeln("\n\n");
194
195
	}
196
197
	/**
198
	 * encrypt files from the given user
199
	 *
200
	 * @param string $uid
201
	 * @param ProgressBar $progress
202
	 * @param string $userCount
203
	 */
204
	protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) {
205
206
		$this->setupUserFS($uid);
207
		$directories = array();
208
		$directories[] = '/' . $uid . '/files';
209
210
		while ($root = array_pop($directories)) {
211
			$content = $this->rootView->getDirectoryContent($root);
212
			foreach ($content as $file) {
213
				// only decrypt files owned by the user
214
				if($file->getStorage()->instanceOfStorage('OC\Files\Storage\Shared')) {
215
					continue;
216
				}
217
				$path = $root . '/' . $file['name'];
218
				if ($this->rootView->is_dir($path)) {
219
					$directories[] = $path;
220
					continue;
221
				} else {
222
					try {
223
						$progress->setMessage("decrypt files for user $userCount: $path");
224
						$progress->advance();
225 View Code Duplication
						if ($file->isEncrypted() === false) {
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...
226
							$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
227
							$progress->advance();
228
						} else {
229
							if ($this->decryptFile($path) === false) {
230
								$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
231
								$progress->advance();
232
							}
233
						}
234
					} catch (\Exception $e) {
235
						if (isset($this->failed[$uid])) {
236
							$this->failed[$uid][] = $path;
237
						} else {
238
							$this->failed[$uid] = [$path];
239
						}
240
					}
241
				}
242
			}
243
		}
244
	}
245
246
	/**
247
	 * encrypt file
248
	 *
249
	 * @param string $path
250
	 * @return bool
251
	 */
252 View Code Duplication
	protected function decryptFile($path) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
253
254
		$source = $path;
255
		$target = $path . '.decrypted.' . $this->getTimestamp();
256
257
		try {
258
			$this->rootView->copy($source, $target);
259
			$this->rootView->rename($target, $source);
260
		} catch (DecryptionFailedException $e) {
261
			if ($this->rootView->file_exists($target)) {
262
				$this->rootView->unlink($target);
263
			}
264
			return false;
265
		}
266
267
		return true;
268
	}
269
270
	/**
271
	 * get current timestamp
272
	 *
273
	 * @return int
274
	 */
275
	protected function getTimestamp() {
276
		return time();
277
	}
278
279
280
	/**
281
	 * setup user file system
282
	 *
283
	 * @param string $uid
284
	 */
285
	protected function setupUserFS($uid) {
286
		\OC_Util::tearDownFS();
287
		\OC_Util::setupFS($uid);
288
	}
289
290
}
291