Completed
Push — stable8.1 ( c01ad6...6cfe33 )
by
unknown
67:28
created

UserHooks   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 73.79%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 29
c 1
b 0
f 0
lcom 1
cbo 14
dl 0
loc 270
ccs 76
cts 103
cp 0.7379
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A login() 0 18 4
A logout() 0 3 1
A preSetPassphrase() 0 10 4
A addHooks() 0 20 1
A postCreateUser() 0 6 2
A postDeleteUser() 0 6 2
A __construct() 0 20 1
C setPassphrase() 0 65 12
A initMountPoints() 0 3 1
A postPasswordReset() 0 6 1
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Clark Tomlinson <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2015, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\Encryption\Hooks;
25
26
27
use OC\Files\Filesystem;
28
use OCP\IUserManager;
29
use OCP\Util as OCUtil;
30
use OCA\Encryption\Hooks\Contracts\IHook;
31
use OCA\Encryption\KeyManager;
32
use OCA\Encryption\Crypto\Crypt;
33
use OCA\Encryption\Users\Setup;
34
use OCP\App;
35
use OCP\ILogger;
36
use OCP\IUserSession;
37
use OCA\Encryption\Util;
38
use OCA\Encryption\Session;
39
use OCA\Encryption\Recovery;
40
41
class UserHooks implements IHook {
42
	/**
43
	 * @var KeyManager
44
	 */
45
	private $keyManager;
46
	/**
47
	 * @var IUserManager
48
	 */
49
	private $userManager;
50
	/**
51
	 * @var ILogger
52
	 */
53
	private $logger;
54
	/**
55
	 * @var Setup
56
	 */
57
	private $userSetup;
58
	/**
59
	 * @var IUserSession
60
	 */
61
	private $user;
62
	/**
63
	 * @var Util
64
	 */
65
	private $util;
66
	/**
67
	 * @var Session
68
	 */
69
	private $session;
70
	/**
71
	 * @var Recovery
72
	 */
73
	private $recovery;
74
	/**
75
	 * @var Crypt
76
	 */
77
	private $crypt;
78
79
	/**
80
	 * UserHooks constructor.
81
	 *
82
	 * @param KeyManager $keyManager
83
	 * @param IUserManager $userManager
84
	 * @param ILogger $logger
85
	 * @param Setup $userSetup
86
	 * @param IUserSession $user
87
	 * @param Util $util
88
	 * @param Session $session
89
	 * @param Crypt $crypt
90
	 * @param Recovery $recovery
91
	 */
92 9
	public function __construct(KeyManager $keyManager,
93
								IUserManager $userManager,
94
								ILogger $logger,
95
								Setup $userSetup,
96
								IUserSession $user,
97
								Util $util,
98
								Session $session,
99
								Crypt $crypt,
100
								Recovery $recovery) {
101
102 9
		$this->keyManager = $keyManager;
103 9
		$this->userManager = $userManager;
104 9
		$this->logger = $logger;
105 9
		$this->userSetup = $userSetup;
106 9
		$this->user = $user;
107 9
		$this->util = $util;
108 9
		$this->session = $session;
109 9
		$this->recovery = $recovery;
110 9
		$this->crypt = $crypt;
111 9
	}
112
113
	/**
114
	 * Connects Hooks
115
	 *
116
	 * @return null
117
	 */
118
	public function addHooks() {
119
		OCUtil::connectHook('OC_User', 'post_login', $this, 'login');
120
		OCUtil::connectHook('OC_User', 'logout', $this, 'logout');
121
		OCUtil::connectHook('OC_User',
122
			'post_setPassword',
123
			$this,
124
			'setPassphrase');
125
		OCUtil::connectHook('OC_User',
126
			'pre_setPassword',
127
			$this,
128
			'preSetPassphrase');
129
		OCUtil::connectHook('OC_User',
130
			'post_createUser',
131
			$this,
132
			'postCreateUser');
133
		OCUtil::connectHook('OC_User',
134
			'post_deleteUser',
135
			$this,
136
			'postDeleteUser');
137
	}
138
139
140
	/**
141
	 * Startup encryption backend upon user login
142
	 *
143
	 * @note This method should never be called for users using client side encryption
144
	 * @param array $params
145
	 * @return bool
146
	 */
147 1
	public function login($params) {
148
149 1
		if (!App::isEnabled('encryption')) {
150
			return true;
151
		}
152
153
		// ensure filesystem is loaded
154 1
		if (!\OC\Files\Filesystem::$loaded) {
155
			\OC_Util::setupFS($params['uid']);
156
		}
157
158
		// setup user, if user not ready force relogin
159 1
		if (!$this->userSetup->setupUser($params['uid'], $params['password'])) {
160 1
			return false;
161
		}
162
163 1
		$this->keyManager->init($params['uid'], $params['password']);
164 1
	}
165
166
	/**
167
	 * remove keys from session during logout
168
	 */
169 1
	public function logout() {
170 1
		$this->session->clear();
171 1
	}
172
173
	/**
174
	 * setup encryption backend upon user created
175
	 *
176
	 * @note This method should never be called for users using client side encryption
177
	 * @param array $params
178
	 */
179 1
	public function postCreateUser($params) {
180
181 1
		if (App::isEnabled('encryption')) {
182 1
			$this->userSetup->setupUser($params['uid'], $params['password']);
183 1
		}
184 1
	}
185
186
	/**
187
	 * cleanup encryption backend upon user deleted
188
	 *
189
	 * @param array $params : uid, password
190
	 * @note This method should never be called for users using client side encryption
191
	 */
192 1
	public function postDeleteUser($params) {
193
194 1
		if (App::isEnabled('encryption')) {
195 1
			$this->keyManager->deletePublicKey($params['uid']);
196 1
		}
197 1
	}
198
199
	/**
200
	 * If the password can't be changed within ownCloud, than update the key password in advance.
201
	 *
202
	 * @param array $params : uid, password
203
	 * @return bool
204
	 */
205 2
	public function preSetPassphrase($params) {
206 2
		if (App::isEnabled('encryption')) {
207
208 2
			$user = $this->userManager->get($params['uid']);
209
210 2
			if ($user && !$user->canChangePassword()) {
211 1
				$this->setPassphrase($params);
212 1
			}
213 2
		}
214 2
	}
215
216
	/**
217
	 * Change a user's encryption passphrase
218
	 *
219
	 * @param array $params keys: uid, password
220
	 * @return bool
221
	 */
222 2
	public function setPassphrase($params) {
223
224
		// Get existing decrypted private key
225 2
		$privateKey = $this->session->getPrivateKey();
226 2
		$user = $this->user->getUser();
227
228
		// current logged in user changes his own password
229 2
		if ($user && $params['uid'] === $user->getUID() && $privateKey) {
230
231
			// Encrypt private key with new user pwd as passphrase
232 1
			$encryptedPrivateKey = $this->crypt->symmetricEncryptFileContent($privateKey,
233 1
				$params['password']);
234
235
			// Save private key
236 1
			if ($encryptedPrivateKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encryptedPrivateKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
237 1
				$this->keyManager->setPrivateKey($this->user->getUser()->getUID(),
238 1
					$this->crypt->generateHeader() . $encryptedPrivateKey);
239 1
			} else {
240
				$this->logger->error('Encryption could not update users encryption password');
241
			}
242
243
			// NOTE: Session does not need to be updated as the
244
			// private key has not changed, only the passphrase
245
			// used to decrypt it has changed
246 1
		} else { // admin changed the password for a different user, create new keys and re-encrypt file keys
247 2
			$user = $params['uid'];
248 2
			$this->initMountPoints($user);
249 2
			$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
250
251
			// we generate new keys if...
252
			// ...we have a recovery password and the user enabled the recovery key
253
			// ...encryption was activated for the first time (no keys exists)
254
			// ...the user doesn't have any files
255
			if (
256 2
				($this->recovery->isRecoveryEnabledForUser($user) && $recoveryPassword)
257 2
				|| !$this->keyManager->userHasKeys($user)
258 2
				|| !$this->util->userHasFiles($user)
259 2
			) {
260
261
				// backup old keys
262
				//$this->backupAllKeys('recovery');
263
264 2
				$newUserPassword = $params['password'];
265
266 2
				$keyPair = $this->crypt->createKeyPair();
267
268
				// Save public key
269 2
				$this->keyManager->setPublicKey($user, $keyPair['publicKey']);
270
271
				// Encrypt private key with new password
272 2
				$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
273 2
					$newUserPassword);
274
275 2
				if ($encryptedKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encryptedKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
276 1
					$this->keyManager->setPrivateKey($user, $this->crypt->generateHeader() . $encryptedKey);
277
278 1
					if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
279 1
						$this->recovery->recoverUsersFiles($recoveryPassword, $user);
280 1
					}
281 1
				} else {
282 1
					$this->logger->error('Encryption Could not update users encryption password');
283
				}
284 2
			}
285
		}
286 2
	}
287
288
	/**
289
	 * init mount points for given user
290
	 *
291
	 * @param string $user
292
	 * @throws \OC\User\NoUserException
293
	 */
294
	protected function initMountPoints($user) {
295
		Filesystem::initMountPoints($user);
296
	}
297
298
299
	/**
300
	 * after password reset we create a new key pair for the user
301
	 *
302
	 * @param array $params
303
	 */
304 1
	public function postPasswordReset($params) {
305 1
		$password = $params['password'];
306
307 1
		$this->keyManager->replaceUserKeys($params['uid']);
308 1
		$this->userSetup->setupServerSide($params['uid'], $password);
309 1
	}
310
}
311