Passed
Push — master ( d2bc85...273849 )
by Morris
13:10
created

DecryptAll::decryptAll()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 4
nop 3
dl 0
loc 33
rs 8.6506
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Christian Jürges <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
28
namespace OC\Encryption;
29
30
use OC\Encryption\Exceptions\DecryptionFailedException;
31
use OC\Files\View;
32
use \OCP\Encryption\IEncryptionModule;
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
	/**
59
	 * @param Manager $encryptionManager
60
	 * @param IUserManager $userManager
61
	 * @param View $rootView
62
	 */
63
	public function __construct(
64
		Manager $encryptionManager,
65
		IUserManager $userManager,
66
		View $rootView
67
	) {
68
		$this->encryptionManager = $encryptionManager;
69
		$this->userManager = $userManager;
70
		$this->rootView = $rootView;
71
		$this->failed = [];
72
	}
73
74
	/**
75
	 * start to decrypt all files
76
	 *
77
	 * @param InputInterface $input
78
	 * @param OutputInterface $output
79
	 * @param string $user which users data folder should be decrypted, default = all users
80
	 * @return bool
81
	 * @throws \Exception
82
	 */
83
	public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') {
84
85
		$this->input = $input;
86
		$this->output = $output;
87
88
		if ($user !== '' && $this->userManager->userExists($user) === false) {
89
			$this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again');
90
			return false;
91
		}
92
93
		$this->output->writeln('prepare encryption modules...');
94
		if ($this->prepareEncryptionModules($user) === false) {
95
			return false;
96
		}
97
		$this->output->writeln(' done.');
98
99
		$this->decryptAllUsersFiles($user);
100
101
		if (empty($this->failed)) {
102
			$this->output->writeln('all files could be decrypted successfully!');
103
		} else {
104
			$this->output->writeln('Files for following users couldn\'t be decrypted, ');
105
			$this->output->writeln('maybe the user is not set up in a way that supports this operation: ');
106
			foreach ($this->failed as $uid => $paths) {
107
				$this->output->writeln('    ' . $uid);
108
				foreach ($paths as $path) {
109
					$this->output->writeln('        ' . $path);
110
				}
111
			}
112
			$this->output->writeln('');
113
		}
114
115
		return true;
116
	}
117
118
	/**
119
	 * prepare encryption modules to perform the decrypt all function
120
	 *
121
	 * @param $user
122
	 * @return bool
123
	 */
124
	protected function prepareEncryptionModules($user) {
125
		// prepare all encryption modules for decrypt all
126
		$encryptionModules = $this->encryptionManager->getEncryptionModules();
127
		foreach ($encryptionModules as $moduleDesc) {
128
			/** @var IEncryptionModule $module */
129
			$module = call_user_func($moduleDesc['callback']);
130
			$this->output->writeln('');
131
			$this->output->writeln('Prepare "' . $module->getDisplayName() . '"');
132
			$this->output->writeln('');
133
			if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) {
134
				$this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!');
135
				return false;
136
			}
137
		}
138
139
		return true;
140
	}
141
142
	/**
143
	 * iterate over all user and encrypt their files
144
	 *
145
	 * @param string $user which users files should be decrypted, default = all users
146
	 */
147
	protected function decryptAllUsersFiles($user = '') {
148
149
		$this->output->writeln("\n");
150
151
		$userList = [];
152
		if ($user === '') {
153
154
			$fetchUsersProgress = new ProgressBar($this->output);
155
			$fetchUsersProgress->setFormat(" %message% \n [%bar%]");
156
			$fetchUsersProgress->start();
157
			$fetchUsersProgress->setMessage("Fetch list of users...");
158
			$fetchUsersProgress->advance();
159
160
			foreach ($this->userManager->getBackends() as $backend) {
161
				$limit = 500;
162
				$offset = 0;
163
				do {
164
					$users = $backend->getUsers('', $limit, $offset);
165
					foreach ($users as $user) {
166
						$userList[] = $user;
167
					}
168
					$offset += $limit;
169
					$fetchUsersProgress->advance();
170
				} while (count($users) >= $limit);
171
				$fetchUsersProgress->setMessage("Fetch list of users... finished");
172
				$fetchUsersProgress->finish();
173
			}
174
		} else {
175
			$userList[] = $user;
176
		}
177
178
		$this->output->writeln("\n\n");
179
180
		$progress = new ProgressBar($this->output);
181
		$progress->setFormat(" %message% \n [%bar%]");
182
		$progress->start();
183
		$progress->setMessage("starting to decrypt files...");
184
		$progress->advance();
185
186
		$numberOfUsers = count($userList);
187
		$userNo = 1;
188
		foreach ($userList as $uid) {
189
			$userCount = "$uid ($userNo of $numberOfUsers)";
190
			$this->decryptUsersFiles($uid, $progress, $userCount);
191
			$userNo++;
192
		}
193
194
		$progress->setMessage("starting to decrypt files... finished");
195
		$progress->finish();
196
197
		$this->output->writeln("\n\n");
198
199
	}
200
201
	/**
202
	 * encrypt files from the given user
203
	 *
204
	 * @param string $uid
205
	 * @param ProgressBar $progress
206
	 * @param string $userCount
207
	 */
208
	protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) {
209
210
		$this->setupUserFS($uid);
211
		$directories = array();
212
		$directories[] = '/' . $uid . '/files';
213
214
		while ($root = array_pop($directories)) {
215
			$content = $this->rootView->getDirectoryContent($root);
216
			foreach ($content as $file) {
217
				// only decrypt files owned by the user
218
				if($file->getStorage()->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
219
					continue;
220
				}
221
				$path = $root . '/' . $file['name'];
222
				if ($this->rootView->is_dir($path)) {
223
					$directories[] = $path;
224
					continue;
225
				} else {
226
					try {
227
						$progress->setMessage("decrypt files for user $userCount: $path");
228
						$progress->advance();
229
						if ($file->isEncrypted() === false) {
230
							$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
231
							$progress->advance();
232
						} else {
233
							if ($this->decryptFile($path) === false) {
234
								$progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
235
								$progress->advance();
236
							}
237
						}
238
					} catch (\Exception $e) {
239
						if (isset($this->failed[$uid])) {
240
							$this->failed[$uid][] = $path;
241
						} else {
242
							$this->failed[$uid] = [$path];
243
						}
244
					}
245
				}
246
			}
247
		}
248
	}
249
250
	/**
251
	 * encrypt file
252
	 *
253
	 * @param string $path
254
	 * @return bool
255
	 */
256
	protected function decryptFile($path) {
257
258
		// skip already decrypted files
259
		$fileInfo = $this->rootView->getFileInfo($path);
260
		if ($fileInfo !== false && !$fileInfo->isEncrypted()) {
261
			return true;
262
		}
263
264
		$source = $path;
265
		$target = $path . '.decrypted.' . $this->getTimestamp();
266
267
		try {
268
			$this->rootView->copy($source, $target);
269
			$this->rootView->rename($target, $source);
270
		} catch (DecryptionFailedException $e) {
271
			if ($this->rootView->file_exists($target)) {
272
				$this->rootView->unlink($target);
273
			}
274
			return false;
275
		}
276
277
		return true;
278
	}
279
280
	/**
281
	 * get current timestamp
282
	 *
283
	 * @return int
284
	 */
285
	protected function getTimestamp() {
286
		return time();
287
	}
288
289
290
	/**
291
	 * setup user file system
292
	 *
293
	 * @param string $uid
294
	 */
295
	protected function setupUserFS($uid) {
296
		\OC_Util::tearDownFS();
297
		\OC_Util::setupFS($uid);
298
	}
299
300
}
301