Completed
Push — stable13 ( 9b96db...7edc8c )
by Morris
13:16
created
lib/private/Authentication/TwoFactorAuth/Manager.php 2 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -30,7 +30,6 @@
 block discarded – undo
30 30
 use OC;
31 31
 use OC\App\AppManager;
32 32
 use OC_App;
33
-use OC\Authentication\Exceptions\ExpiredTokenException;
34 33
 use OC\Authentication\Exceptions\InvalidTokenException;
35 34
 use OC\Authentication\Token\IProvider as TokenProvider;
36 35
 use OCP\Activity\IManager;
Please login to merge, or discard this patch.
Indentation   +296 added lines, -296 removed lines patch added patch discarded remove patch
@@ -44,304 +44,304 @@
 block discarded – undo
44 44
 
45 45
 class Manager {
46 46
 
47
-	const SESSION_UID_KEY = 'two_factor_auth_uid';
48
-	const SESSION_UID_DONE = 'two_factor_auth_passed';
49
-	const BACKUP_CODES_APP_ID = 'twofactor_backupcodes';
50
-	const BACKUP_CODES_PROVIDER_ID = 'backup_codes';
51
-	const REMEMBER_LOGIN = 'two_factor_remember_login';
52
-
53
-	/** @var AppManager */
54
-	private $appManager;
55
-
56
-	/** @var ISession */
57
-	private $session;
58
-
59
-	/** @var IConfig */
60
-	private $config;
61
-
62
-	/** @var IManager */
63
-	private $activityManager;
64
-
65
-	/** @var ILogger */
66
-	private $logger;
67
-
68
-	/** @var TokenProvider */
69
-	private $tokenProvider;
70
-
71
-	/** @var ITimeFactory */
72
-	private $timeFactory;
73
-
74
-	/**
75
-	 * @param AppManager $appManager
76
-	 * @param ISession $session
77
-	 * @param IConfig $config
78
-	 * @param IManager $activityManager
79
-	 * @param ILogger $logger
80
-	 * @param TokenProvider $tokenProvider
81
-	 * @param ITimeFactory $timeFactory
82
-	 */
83
-	public function __construct(AppManager $appManager,
84
-								ISession $session,
85
-								IConfig $config,
86
-								IManager $activityManager,
87
-								ILogger $logger,
88
-								TokenProvider $tokenProvider,
89
-								ITimeFactory $timeFactory) {
90
-		$this->appManager = $appManager;
91
-		$this->session = $session;
92
-		$this->config = $config;
93
-		$this->activityManager = $activityManager;
94
-		$this->logger = $logger;
95
-		$this->tokenProvider = $tokenProvider;
96
-		$this->timeFactory = $timeFactory;
97
-	}
98
-
99
-	/**
100
-	 * Determine whether the user must provide a second factor challenge
101
-	 *
102
-	 * @param IUser $user
103
-	 * @return boolean
104
-	 */
105
-	public function isTwoFactorAuthenticated(IUser $user) {
106
-		$twoFactorEnabled = ((int) $this->config->getUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 0)) === 0;
107
-		return $twoFactorEnabled && count($this->getProviders($user)) > 0;
108
-	}
109
-
110
-	/**
111
-	 * Disable 2FA checks for the given user
112
-	 *
113
-	 * @param IUser $user
114
-	 */
115
-	public function disableTwoFactorAuthentication(IUser $user) {
116
-		$this->config->setUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 1);
117
-	}
118
-
119
-	/**
120
-	 * Enable all 2FA checks for the given user
121
-	 *
122
-	 * @param IUser $user
123
-	 */
124
-	public function enableTwoFactorAuthentication(IUser $user) {
125
-		$this->config->deleteUserValue($user->getUID(), 'core', 'two_factor_auth_disabled');
126
-	}
127
-
128
-	/**
129
-	 * Get a 2FA provider by its ID
130
-	 *
131
-	 * @param IUser $user
132
-	 * @param string $challengeProviderId
133
-	 * @return IProvider|null
134
-	 */
135
-	public function getProvider(IUser $user, $challengeProviderId) {
136
-		$providers = $this->getProviders($user, true);
137
-		return isset($providers[$challengeProviderId]) ? $providers[$challengeProviderId] : null;
138
-	}
139
-
140
-	/**
141
-	 * @param IUser $user
142
-	 * @return IProvider|null the backup provider, if enabled for the given user
143
-	 */
144
-	public function getBackupProvider(IUser $user) {
145
-		$providers = $this->getProviders($user, true);
146
-		if (!isset($providers[self::BACKUP_CODES_PROVIDER_ID])) {
147
-			return null;
148
-		}
149
-		return $providers[self::BACKUP_CODES_PROVIDER_ID];
150
-	}
151
-
152
-	/**
153
-	 * Get the list of 2FA providers for the given user
154
-	 *
155
-	 * @param IUser $user
156
-	 * @param bool $includeBackupApp
157
-	 * @return IProvider[]
158
-	 * @throws Exception
159
-	 */
160
-	public function getProviders(IUser $user, $includeBackupApp = false) {
161
-		$allApps = $this->appManager->getEnabledAppsForUser($user);
162
-		$providers = [];
163
-
164
-		foreach ($allApps as $appId) {
165
-			if (!$includeBackupApp && $appId === self::BACKUP_CODES_APP_ID) {
166
-				continue;
167
-			}
168
-
169
-			$info = $this->appManager->getAppInfo($appId);
170
-			if (isset($info['two-factor-providers'])) {
171
-				$providerClasses = $info['two-factor-providers'];
172
-				foreach ($providerClasses as $class) {
173
-					try {
174
-						$this->loadTwoFactorApp($appId);
175
-						$provider = OC::$server->query($class);
176
-						$providers[$provider->getId()] = $provider;
177
-					} catch (QueryException $exc) {
178
-						// Provider class can not be resolved
179
-						throw new Exception("Could not load two-factor auth provider $class");
180
-					}
181
-				}
182
-			}
183
-		}
184
-
185
-		return array_filter($providers, function ($provider) use ($user) {
186
-			/* @var $provider IProvider */
187
-			return $provider->isTwoFactorAuthEnabledForUser($user);
188
-		});
189
-	}
190
-
191
-	/**
192
-	 * Load an app by ID if it has not been loaded yet
193
-	 *
194
-	 * @param string $appId
195
-	 */
196
-	protected function loadTwoFactorApp($appId) {
197
-		if (!OC_App::isAppLoaded($appId)) {
198
-			OC_App::loadApp($appId);
199
-		}
200
-	}
201
-
202
-	/**
203
-	 * Verify the given challenge
204
-	 *
205
-	 * @param string $providerId
206
-	 * @param IUser $user
207
-	 * @param string $challenge
208
-	 * @return boolean
209
-	 */
210
-	public function verifyChallenge($providerId, IUser $user, $challenge) {
211
-		$provider = $this->getProvider($user, $providerId);
212
-		if (is_null($provider)) {
213
-			return false;
214
-		}
215
-
216
-		$passed = $provider->verifyChallenge($user, $challenge);
217
-		if ($passed) {
218
-			if ($this->session->get(self::REMEMBER_LOGIN) === true) {
219
-				// TODO: resolve cyclic dependency and use DI
220
-				\OC::$server->getUserSession()->createRememberMeToken($user);
221
-			}
222
-			$this->session->remove(self::SESSION_UID_KEY);
223
-			$this->session->remove(self::REMEMBER_LOGIN);
224
-			$this->session->set(self::SESSION_UID_DONE, $user->getUID());
225
-
226
-			// Clear token from db
227
-			$sessionId = $this->session->getId();
228
-			$token = $this->tokenProvider->getToken($sessionId);
229
-			$tokenId = $token->getId();
230
-			$this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId);
231
-
232
-			$this->publishEvent($user, 'twofactor_success', [
233
-				'provider' => $provider->getDisplayName(),
234
-			]);
235
-		} else {
236
-			$this->publishEvent($user, 'twofactor_failed', [
237
-				'provider' => $provider->getDisplayName(),
238
-			]);
239
-		}
240
-		return $passed;
241
-	}
242
-
243
-	/**
244
-	 * Push a 2fa event the user's activity stream
245
-	 *
246
-	 * @param IUser $user
247
-	 * @param string $event
248
-	 */
249
-	private function publishEvent(IUser $user, $event, array $params) {
250
-		$activity = $this->activityManager->generateEvent();
251
-		$activity->setApp('core')
252
-			->setType('security')
253
-			->setAuthor($user->getUID())
254
-			->setAffectedUser($user->getUID())
255
-			->setSubject($event, $params);
256
-		try {
257
-			$this->activityManager->publish($activity);
258
-		} catch (BadMethodCallException $e) {
259
-			$this->logger->warning('could not publish backup code creation activity', ['app' => 'core']);
260
-			$this->logger->logException($e, ['app' => 'core']);
261
-		}
262
-	}
263
-
264
-	/**
265
-	 * Check if the currently logged in user needs to pass 2FA
266
-	 *
267
-	 * @param IUser $user the currently logged in user
268
-	 * @return boolean
269
-	 */
270
-	public function needsSecondFactor(IUser $user = null) {
271
-		if ($user === null) {
272
-			return false;
273
-		}
274
-
275
-		// If we are authenticated using an app password skip all this
276
-		if ($this->session->exists('app_password')) {
277
-			return false;
278
-		}
279
-
280
-		// First check if the session tells us we should do 2FA (99% case)
281
-		if (!$this->session->exists(self::SESSION_UID_KEY)) {
282
-
283
-			// Check if the session tells us it is 2FA authenticated already
284
-			if ($this->session->exists(self::SESSION_UID_DONE) &&
285
-				$this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
286
-				return false;
287
-			}
288
-
289
-			/*
47
+    const SESSION_UID_KEY = 'two_factor_auth_uid';
48
+    const SESSION_UID_DONE = 'two_factor_auth_passed';
49
+    const BACKUP_CODES_APP_ID = 'twofactor_backupcodes';
50
+    const BACKUP_CODES_PROVIDER_ID = 'backup_codes';
51
+    const REMEMBER_LOGIN = 'two_factor_remember_login';
52
+
53
+    /** @var AppManager */
54
+    private $appManager;
55
+
56
+    /** @var ISession */
57
+    private $session;
58
+
59
+    /** @var IConfig */
60
+    private $config;
61
+
62
+    /** @var IManager */
63
+    private $activityManager;
64
+
65
+    /** @var ILogger */
66
+    private $logger;
67
+
68
+    /** @var TokenProvider */
69
+    private $tokenProvider;
70
+
71
+    /** @var ITimeFactory */
72
+    private $timeFactory;
73
+
74
+    /**
75
+     * @param AppManager $appManager
76
+     * @param ISession $session
77
+     * @param IConfig $config
78
+     * @param IManager $activityManager
79
+     * @param ILogger $logger
80
+     * @param TokenProvider $tokenProvider
81
+     * @param ITimeFactory $timeFactory
82
+     */
83
+    public function __construct(AppManager $appManager,
84
+                                ISession $session,
85
+                                IConfig $config,
86
+                                IManager $activityManager,
87
+                                ILogger $logger,
88
+                                TokenProvider $tokenProvider,
89
+                                ITimeFactory $timeFactory) {
90
+        $this->appManager = $appManager;
91
+        $this->session = $session;
92
+        $this->config = $config;
93
+        $this->activityManager = $activityManager;
94
+        $this->logger = $logger;
95
+        $this->tokenProvider = $tokenProvider;
96
+        $this->timeFactory = $timeFactory;
97
+    }
98
+
99
+    /**
100
+     * Determine whether the user must provide a second factor challenge
101
+     *
102
+     * @param IUser $user
103
+     * @return boolean
104
+     */
105
+    public function isTwoFactorAuthenticated(IUser $user) {
106
+        $twoFactorEnabled = ((int) $this->config->getUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 0)) === 0;
107
+        return $twoFactorEnabled && count($this->getProviders($user)) > 0;
108
+    }
109
+
110
+    /**
111
+     * Disable 2FA checks for the given user
112
+     *
113
+     * @param IUser $user
114
+     */
115
+    public function disableTwoFactorAuthentication(IUser $user) {
116
+        $this->config->setUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 1);
117
+    }
118
+
119
+    /**
120
+     * Enable all 2FA checks for the given user
121
+     *
122
+     * @param IUser $user
123
+     */
124
+    public function enableTwoFactorAuthentication(IUser $user) {
125
+        $this->config->deleteUserValue($user->getUID(), 'core', 'two_factor_auth_disabled');
126
+    }
127
+
128
+    /**
129
+     * Get a 2FA provider by its ID
130
+     *
131
+     * @param IUser $user
132
+     * @param string $challengeProviderId
133
+     * @return IProvider|null
134
+     */
135
+    public function getProvider(IUser $user, $challengeProviderId) {
136
+        $providers = $this->getProviders($user, true);
137
+        return isset($providers[$challengeProviderId]) ? $providers[$challengeProviderId] : null;
138
+    }
139
+
140
+    /**
141
+     * @param IUser $user
142
+     * @return IProvider|null the backup provider, if enabled for the given user
143
+     */
144
+    public function getBackupProvider(IUser $user) {
145
+        $providers = $this->getProviders($user, true);
146
+        if (!isset($providers[self::BACKUP_CODES_PROVIDER_ID])) {
147
+            return null;
148
+        }
149
+        return $providers[self::BACKUP_CODES_PROVIDER_ID];
150
+    }
151
+
152
+    /**
153
+     * Get the list of 2FA providers for the given user
154
+     *
155
+     * @param IUser $user
156
+     * @param bool $includeBackupApp
157
+     * @return IProvider[]
158
+     * @throws Exception
159
+     */
160
+    public function getProviders(IUser $user, $includeBackupApp = false) {
161
+        $allApps = $this->appManager->getEnabledAppsForUser($user);
162
+        $providers = [];
163
+
164
+        foreach ($allApps as $appId) {
165
+            if (!$includeBackupApp && $appId === self::BACKUP_CODES_APP_ID) {
166
+                continue;
167
+            }
168
+
169
+            $info = $this->appManager->getAppInfo($appId);
170
+            if (isset($info['two-factor-providers'])) {
171
+                $providerClasses = $info['two-factor-providers'];
172
+                foreach ($providerClasses as $class) {
173
+                    try {
174
+                        $this->loadTwoFactorApp($appId);
175
+                        $provider = OC::$server->query($class);
176
+                        $providers[$provider->getId()] = $provider;
177
+                    } catch (QueryException $exc) {
178
+                        // Provider class can not be resolved
179
+                        throw new Exception("Could not load two-factor auth provider $class");
180
+                    }
181
+                }
182
+            }
183
+        }
184
+
185
+        return array_filter($providers, function ($provider) use ($user) {
186
+            /* @var $provider IProvider */
187
+            return $provider->isTwoFactorAuthEnabledForUser($user);
188
+        });
189
+    }
190
+
191
+    /**
192
+     * Load an app by ID if it has not been loaded yet
193
+     *
194
+     * @param string $appId
195
+     */
196
+    protected function loadTwoFactorApp($appId) {
197
+        if (!OC_App::isAppLoaded($appId)) {
198
+            OC_App::loadApp($appId);
199
+        }
200
+    }
201
+
202
+    /**
203
+     * Verify the given challenge
204
+     *
205
+     * @param string $providerId
206
+     * @param IUser $user
207
+     * @param string $challenge
208
+     * @return boolean
209
+     */
210
+    public function verifyChallenge($providerId, IUser $user, $challenge) {
211
+        $provider = $this->getProvider($user, $providerId);
212
+        if (is_null($provider)) {
213
+            return false;
214
+        }
215
+
216
+        $passed = $provider->verifyChallenge($user, $challenge);
217
+        if ($passed) {
218
+            if ($this->session->get(self::REMEMBER_LOGIN) === true) {
219
+                // TODO: resolve cyclic dependency and use DI
220
+                \OC::$server->getUserSession()->createRememberMeToken($user);
221
+            }
222
+            $this->session->remove(self::SESSION_UID_KEY);
223
+            $this->session->remove(self::REMEMBER_LOGIN);
224
+            $this->session->set(self::SESSION_UID_DONE, $user->getUID());
225
+
226
+            // Clear token from db
227
+            $sessionId = $this->session->getId();
228
+            $token = $this->tokenProvider->getToken($sessionId);
229
+            $tokenId = $token->getId();
230
+            $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId);
231
+
232
+            $this->publishEvent($user, 'twofactor_success', [
233
+                'provider' => $provider->getDisplayName(),
234
+            ]);
235
+        } else {
236
+            $this->publishEvent($user, 'twofactor_failed', [
237
+                'provider' => $provider->getDisplayName(),
238
+            ]);
239
+        }
240
+        return $passed;
241
+    }
242
+
243
+    /**
244
+     * Push a 2fa event the user's activity stream
245
+     *
246
+     * @param IUser $user
247
+     * @param string $event
248
+     */
249
+    private function publishEvent(IUser $user, $event, array $params) {
250
+        $activity = $this->activityManager->generateEvent();
251
+        $activity->setApp('core')
252
+            ->setType('security')
253
+            ->setAuthor($user->getUID())
254
+            ->setAffectedUser($user->getUID())
255
+            ->setSubject($event, $params);
256
+        try {
257
+            $this->activityManager->publish($activity);
258
+        } catch (BadMethodCallException $e) {
259
+            $this->logger->warning('could not publish backup code creation activity', ['app' => 'core']);
260
+            $this->logger->logException($e, ['app' => 'core']);
261
+        }
262
+    }
263
+
264
+    /**
265
+     * Check if the currently logged in user needs to pass 2FA
266
+     *
267
+     * @param IUser $user the currently logged in user
268
+     * @return boolean
269
+     */
270
+    public function needsSecondFactor(IUser $user = null) {
271
+        if ($user === null) {
272
+            return false;
273
+        }
274
+
275
+        // If we are authenticated using an app password skip all this
276
+        if ($this->session->exists('app_password')) {
277
+            return false;
278
+        }
279
+
280
+        // First check if the session tells us we should do 2FA (99% case)
281
+        if (!$this->session->exists(self::SESSION_UID_KEY)) {
282
+
283
+            // Check if the session tells us it is 2FA authenticated already
284
+            if ($this->session->exists(self::SESSION_UID_DONE) &&
285
+                $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
286
+                return false;
287
+            }
288
+
289
+            /*
290 290
 			 * If the session is expired check if we are not logged in by a token
291 291
 			 * that still needs 2FA auth
292 292
 			 */
293
-			try {
294
-				$sessionId = $this->session->getId();
295
-				$token = $this->tokenProvider->getToken($sessionId);
296
-				$tokenId = $token->getId();
297
-				$tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
298
-
299
-				if (!in_array($tokenId, $tokensNeeding2FA, true)) {
300
-					$this->session->set(self::SESSION_UID_DONE, $user->getUID());
301
-					return false;
302
-				}
303
-			} catch (InvalidTokenException $e) {
304
-			}
305
-		}
306
-
307
-		if (!$this->isTwoFactorAuthenticated($user)) {
308
-			// There is no second factor any more -> let the user pass
309
-			//   This prevents infinite redirect loops when a user is about
310
-			//   to solve the 2FA challenge, and the provider app is
311
-			//   disabled the same time
312
-			$this->session->remove(self::SESSION_UID_KEY);
313
-
314
-			$keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
315
-			foreach ($keys as $key) {
316
-				$this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key);
317
-			}
318
-			return false;
319
-		}
320
-
321
-		return true;
322
-	}
323
-
324
-	/**
325
-	 * Prepare the 2FA login
326
-	 *
327
-	 * @param IUser $user
328
-	 * @param boolean $rememberMe
329
-	 */
330
-	public function prepareTwoFactorLogin(IUser $user, $rememberMe) {
331
-		$this->session->set(self::SESSION_UID_KEY, $user->getUID());
332
-		$this->session->set(self::REMEMBER_LOGIN, $rememberMe);
333
-
334
-		$id = $this->session->getId();
335
-		$token = $this->tokenProvider->getToken($id);
336
-		$this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime());
337
-	}
338
-
339
-	public function clearTwoFactorPending(string $userId) {
340
-		$tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa');
341
-
342
-		foreach ($tokensNeeding2FA as $tokenId) {
343
-			$this->tokenProvider->invalidateTokenById($userId, $tokenId);
344
-		}
345
-	}
293
+            try {
294
+                $sessionId = $this->session->getId();
295
+                $token = $this->tokenProvider->getToken($sessionId);
296
+                $tokenId = $token->getId();
297
+                $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
298
+
299
+                if (!in_array($tokenId, $tokensNeeding2FA, true)) {
300
+                    $this->session->set(self::SESSION_UID_DONE, $user->getUID());
301
+                    return false;
302
+                }
303
+            } catch (InvalidTokenException $e) {
304
+            }
305
+        }
306
+
307
+        if (!$this->isTwoFactorAuthenticated($user)) {
308
+            // There is no second factor any more -> let the user pass
309
+            //   This prevents infinite redirect loops when a user is about
310
+            //   to solve the 2FA challenge, and the provider app is
311
+            //   disabled the same time
312
+            $this->session->remove(self::SESSION_UID_KEY);
313
+
314
+            $keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
315
+            foreach ($keys as $key) {
316
+                $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key);
317
+            }
318
+            return false;
319
+        }
320
+
321
+        return true;
322
+    }
323
+
324
+    /**
325
+     * Prepare the 2FA login
326
+     *
327
+     * @param IUser $user
328
+     * @param boolean $rememberMe
329
+     */
330
+    public function prepareTwoFactorLogin(IUser $user, $rememberMe) {
331
+        $this->session->set(self::SESSION_UID_KEY, $user->getUID());
332
+        $this->session->set(self::REMEMBER_LOGIN, $rememberMe);
333
+
334
+        $id = $this->session->getId();
335
+        $token = $this->tokenProvider->getToken($id);
336
+        $this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime());
337
+    }
338
+
339
+    public function clearTwoFactorPending(string $userId) {
340
+        $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa');
341
+
342
+        foreach ($tokensNeeding2FA as $tokenId) {
343
+            $this->tokenProvider->invalidateTokenById($userId, $tokenId);
344
+        }
345
+    }
346 346
 
347 347
 }
Please login to merge, or discard this patch.
core/Controller/LostController.php 1 patch
Indentation   +331 added lines, -331 removed lines patch added patch discarded remove patch
@@ -58,335 +58,335 @@
 block discarded – undo
58 58
  * @package OC\Core\Controller
59 59
  */
60 60
 class LostController extends Controller {
61
-	/** @var IURLGenerator */
62
-	protected $urlGenerator;
63
-	/** @var IUserManager */
64
-	protected $userManager;
65
-	/** @var Defaults */
66
-	protected $defaults;
67
-	/** @var IL10N */
68
-	protected $l10n;
69
-	/** @var string */
70
-	protected $from;
71
-	/** @var IManager */
72
-	protected $encryptionManager;
73
-	/** @var IConfig */
74
-	protected $config;
75
-	/** @var ISecureRandom */
76
-	protected $secureRandom;
77
-	/** @var IMailer */
78
-	protected $mailer;
79
-	/** @var ITimeFactory */
80
-	protected $timeFactory;
81
-	/** @var ICrypto */
82
-	protected $crypto;
83
-	/** @var Manager */
84
-	private $twoFactorManager;
85
-
86
-	/**
87
-	 * @param string $appName
88
-	 * @param IRequest $request
89
-	 * @param IURLGenerator $urlGenerator
90
-	 * @param IUserManager $userManager
91
-	 * @param Defaults $defaults
92
-	 * @param IL10N $l10n
93
-	 * @param IConfig $config
94
-	 * @param ISecureRandom $secureRandom
95
-	 * @param string $defaultMailAddress
96
-	 * @param IManager $encryptionManager
97
-	 * @param IMailer $mailer
98
-	 * @param ITimeFactory $timeFactory
99
-	 * @param ICrypto $crypto
100
-	 */
101
-	public function __construct($appName,
102
-								IRequest $request,
103
-								IURLGenerator $urlGenerator,
104
-								IUserManager $userManager,
105
-								Defaults $defaults,
106
-								IL10N $l10n,
107
-								IConfig $config,
108
-								ISecureRandom $secureRandom,
109
-								$defaultMailAddress,
110
-								IManager $encryptionManager,
111
-								IMailer $mailer,
112
-								ITimeFactory $timeFactory,
113
-								ICrypto $crypto,
114
-								Manager $twoFactorManager) {
115
-		parent::__construct($appName, $request);
116
-		$this->urlGenerator = $urlGenerator;
117
-		$this->userManager = $userManager;
118
-		$this->defaults = $defaults;
119
-		$this->l10n = $l10n;
120
-		$this->secureRandom = $secureRandom;
121
-		$this->from = $defaultMailAddress;
122
-		$this->encryptionManager = $encryptionManager;
123
-		$this->config = $config;
124
-		$this->mailer = $mailer;
125
-		$this->timeFactory = $timeFactory;
126
-		$this->crypto = $crypto;
127
-		$this->twoFactorManager = $twoFactorManager;
128
-	}
129
-
130
-	/**
131
-	 * Someone wants to reset their password:
132
-	 *
133
-	 * @PublicPage
134
-	 * @NoCSRFRequired
135
-	 *
136
-	 * @param string $token
137
-	 * @param string $userId
138
-	 * @return TemplateResponse
139
-	 */
140
-	public function resetform($token, $userId) {
141
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
142
-			return new TemplateResponse('core', 'error', [
143
-					'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
144
-				],
145
-				'guest'
146
-			);
147
-		}
148
-
149
-		try {
150
-			$this->checkPasswordResetToken($token, $userId);
151
-		} catch (\Exception $e) {
152
-			return new TemplateResponse(
153
-				'core', 'error', [
154
-					"errors" => array(array("error" => $e->getMessage()))
155
-				],
156
-				'guest'
157
-			);
158
-		}
159
-
160
-		return new TemplateResponse(
161
-			'core',
162
-			'lostpassword/resetpassword',
163
-			array(
164
-				'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
165
-			),
166
-			'guest'
167
-		);
168
-	}
169
-
170
-	/**
171
-	 * @param string $token
172
-	 * @param string $userId
173
-	 * @throws \Exception
174
-	 */
175
-	protected function checkPasswordResetToken($token, $userId) {
176
-		$user = $this->userManager->get($userId);
177
-		if($user === null || !$user->isEnabled()) {
178
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
179
-		}
180
-
181
-		try {
182
-			$encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
183
-			$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
184
-			$decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
185
-		} catch (\Exception $e) {
186
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
187
-		}
188
-
189
-		$splittedToken = explode(':', $decryptedToken);
190
-		if(count($splittedToken) !== 2) {
191
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
192
-		}
193
-
194
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*24*7) ||
195
-			$user->getLastLogin() > $splittedToken[0]) {
196
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
197
-		}
198
-
199
-		if (!hash_equals($splittedToken[1], $token)) {
200
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
201
-		}
202
-	}
203
-
204
-	/**
205
-	 * @param $message
206
-	 * @param array $additional
207
-	 * @return array
208
-	 */
209
-	private function error($message, array $additional=array()) {
210
-		return array_merge(array('status' => 'error', 'msg' => $message), $additional);
211
-	}
212
-
213
-	/**
214
-	 * @return array
215
-	 */
216
-	private function success() {
217
-		return array('status'=>'success');
218
-	}
219
-
220
-	/**
221
-	 * @PublicPage
222
-	 * @BruteForceProtection(action=passwordResetEmail)
223
-	 * @AnonRateThrottle(limit=10, period=300)
224
-	 *
225
-	 * @param string $user
226
-	 * @return JSONResponse
227
-	 */
228
-	public function email($user){
229
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
230
-			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
231
-		}
232
-
233
-		\OCP\Util::emitHook(
234
-			'\OCA\Files_Sharing\API\Server2Server',
235
-			'preLoginNameUsedAsUserName',
236
-			['uid' => &$user]
237
-		);
238
-
239
-		// FIXME: use HTTP error codes
240
-		try {
241
-			$this->sendEmail($user);
242
-		} catch (\Exception $e){
243
-			$response = new JSONResponse($this->error($e->getMessage()));
244
-			$response->throttle();
245
-			return $response;
246
-		}
247
-
248
-		$response = new JSONResponse($this->success());
249
-		$response->throttle();
250
-		return $response;
251
-	}
252
-
253
-	/**
254
-	 * @PublicPage
255
-	 * @param string $token
256
-	 * @param string $userId
257
-	 * @param string $password
258
-	 * @param boolean $proceed
259
-	 * @return array
260
-	 */
261
-	public function setPassword($token, $userId, $password, $proceed) {
262
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
263
-			return $this->error($this->l10n->t('Password reset is disabled'));
264
-		}
265
-
266
-		if ($this->encryptionManager->isEnabled() && !$proceed) {
267
-			$encryptionModules = $this->encryptionManager->getEncryptionModules();
268
-			foreach ($encryptionModules as $module) {
269
-				/** @var IEncryptionModule $instance */
270
-				$instance = call_user_func($module['callback']);
271
-				// this way we can find out whether per-user keys are used or a system wide encryption key
272
-				if ($instance->needDetailedAccessList()) {
273
-					return $this->error('', array('encryption' => true));
274
-				}
275
-			}
276
-		}
277
-
278
-		try {
279
-			$this->checkPasswordResetToken($token, $userId);
280
-			$user = $this->userManager->get($userId);
281
-
282
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
283
-
284
-			if (!$user->setPassword($password)) {
285
-				throw new \Exception();
286
-			}
287
-
288
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
289
-
290
-			$this->twoFactorManager->clearTwoFactorPending($userId);
291
-
292
-			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
293
-			@\OC::$server->getUserSession()->unsetMagicInCookie();
294
-		} catch (HintException $e){
295
-			return $this->error($e->getHint());
296
-		} catch (\Exception $e){
297
-			return $this->error($e->getMessage());
298
-		}
299
-
300
-		return $this->success();
301
-	}
302
-
303
-	/**
304
-	 * @param string $input
305
-	 * @throws \Exception
306
-	 */
307
-	protected function sendEmail($input) {
308
-		$user = $this->findUserByIdOrMail($input);
309
-		$email = $user->getEMailAddress();
310
-
311
-		if (empty($email)) {
312
-			throw new \Exception(
313
-				$this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
314
-			);
315
-		}
316
-
317
-		// Generate the token. It is stored encrypted in the database with the
318
-		// secret being the users' email address appended with the system secret.
319
-		// This makes the token automatically invalidate once the user changes
320
-		// their email address.
321
-		$token = $this->secureRandom->generate(
322
-			21,
323
-			ISecureRandom::CHAR_DIGITS.
324
-			ISecureRandom::CHAR_LOWER.
325
-			ISecureRandom::CHAR_UPPER
326
-		);
327
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
328
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
329
-		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
330
-
331
-		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
332
-
333
-		$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
334
-			'link' => $link,
335
-		]);
336
-
337
-		$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
338
-		$emailTemplate->addHeader();
339
-		$emailTemplate->addHeading($this->l10n->t('Password reset'));
340
-
341
-		$emailTemplate->addBodyText(
342
-			$this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.'),
343
-			$this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
344
-		);
345
-
346
-		$emailTemplate->addBodyButton(
347
-			$this->l10n->t('Reset your password'),
348
-			$link,
349
-			false
350
-		);
351
-		$emailTemplate->addFooter();
352
-
353
-		try {
354
-			$message = $this->mailer->createMessage();
355
-			$message->setTo([$email => $user->getUID()]);
356
-			$message->setFrom([$this->from => $this->defaults->getName()]);
357
-			$message->useTemplate($emailTemplate);
358
-			$this->mailer->send($message);
359
-		} catch (\Exception $e) {
360
-			throw new \Exception($this->l10n->t(
361
-				'Couldn\'t send reset email. Please contact your administrator.'
362
-			));
363
-		}
364
-	}
365
-
366
-	/**
367
-	 * @param string $input
368
-	 * @return IUser
369
-	 * @throws \InvalidArgumentException
370
-	 */
371
-	protected function findUserByIdOrMail($input) {
372
-		$user = $this->userManager->get($input);
373
-		if ($user instanceof IUser) {
374
-			if (!$user->isEnabled()) {
375
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
376
-			}
377
-
378
-			return $user;
379
-		}
380
-		$users = $this->userManager->getByEmail($input);
381
-		if (count($users) === 1) {
382
-			$user = $users[0];
383
-			if (!$user->isEnabled()) {
384
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
385
-			}
386
-
387
-			return $user;
388
-		}
389
-
390
-		throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
391
-	}
61
+    /** @var IURLGenerator */
62
+    protected $urlGenerator;
63
+    /** @var IUserManager */
64
+    protected $userManager;
65
+    /** @var Defaults */
66
+    protected $defaults;
67
+    /** @var IL10N */
68
+    protected $l10n;
69
+    /** @var string */
70
+    protected $from;
71
+    /** @var IManager */
72
+    protected $encryptionManager;
73
+    /** @var IConfig */
74
+    protected $config;
75
+    /** @var ISecureRandom */
76
+    protected $secureRandom;
77
+    /** @var IMailer */
78
+    protected $mailer;
79
+    /** @var ITimeFactory */
80
+    protected $timeFactory;
81
+    /** @var ICrypto */
82
+    protected $crypto;
83
+    /** @var Manager */
84
+    private $twoFactorManager;
85
+
86
+    /**
87
+     * @param string $appName
88
+     * @param IRequest $request
89
+     * @param IURLGenerator $urlGenerator
90
+     * @param IUserManager $userManager
91
+     * @param Defaults $defaults
92
+     * @param IL10N $l10n
93
+     * @param IConfig $config
94
+     * @param ISecureRandom $secureRandom
95
+     * @param string $defaultMailAddress
96
+     * @param IManager $encryptionManager
97
+     * @param IMailer $mailer
98
+     * @param ITimeFactory $timeFactory
99
+     * @param ICrypto $crypto
100
+     */
101
+    public function __construct($appName,
102
+                                IRequest $request,
103
+                                IURLGenerator $urlGenerator,
104
+                                IUserManager $userManager,
105
+                                Defaults $defaults,
106
+                                IL10N $l10n,
107
+                                IConfig $config,
108
+                                ISecureRandom $secureRandom,
109
+                                $defaultMailAddress,
110
+                                IManager $encryptionManager,
111
+                                IMailer $mailer,
112
+                                ITimeFactory $timeFactory,
113
+                                ICrypto $crypto,
114
+                                Manager $twoFactorManager) {
115
+        parent::__construct($appName, $request);
116
+        $this->urlGenerator = $urlGenerator;
117
+        $this->userManager = $userManager;
118
+        $this->defaults = $defaults;
119
+        $this->l10n = $l10n;
120
+        $this->secureRandom = $secureRandom;
121
+        $this->from = $defaultMailAddress;
122
+        $this->encryptionManager = $encryptionManager;
123
+        $this->config = $config;
124
+        $this->mailer = $mailer;
125
+        $this->timeFactory = $timeFactory;
126
+        $this->crypto = $crypto;
127
+        $this->twoFactorManager = $twoFactorManager;
128
+    }
129
+
130
+    /**
131
+     * Someone wants to reset their password:
132
+     *
133
+     * @PublicPage
134
+     * @NoCSRFRequired
135
+     *
136
+     * @param string $token
137
+     * @param string $userId
138
+     * @return TemplateResponse
139
+     */
140
+    public function resetform($token, $userId) {
141
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
142
+            return new TemplateResponse('core', 'error', [
143
+                    'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
144
+                ],
145
+                'guest'
146
+            );
147
+        }
148
+
149
+        try {
150
+            $this->checkPasswordResetToken($token, $userId);
151
+        } catch (\Exception $e) {
152
+            return new TemplateResponse(
153
+                'core', 'error', [
154
+                    "errors" => array(array("error" => $e->getMessage()))
155
+                ],
156
+                'guest'
157
+            );
158
+        }
159
+
160
+        return new TemplateResponse(
161
+            'core',
162
+            'lostpassword/resetpassword',
163
+            array(
164
+                'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
165
+            ),
166
+            'guest'
167
+        );
168
+    }
169
+
170
+    /**
171
+     * @param string $token
172
+     * @param string $userId
173
+     * @throws \Exception
174
+     */
175
+    protected function checkPasswordResetToken($token, $userId) {
176
+        $user = $this->userManager->get($userId);
177
+        if($user === null || !$user->isEnabled()) {
178
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
179
+        }
180
+
181
+        try {
182
+            $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
183
+            $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
184
+            $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
185
+        } catch (\Exception $e) {
186
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
187
+        }
188
+
189
+        $splittedToken = explode(':', $decryptedToken);
190
+        if(count($splittedToken) !== 2) {
191
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
192
+        }
193
+
194
+        if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*24*7) ||
195
+            $user->getLastLogin() > $splittedToken[0]) {
196
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
197
+        }
198
+
199
+        if (!hash_equals($splittedToken[1], $token)) {
200
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
201
+        }
202
+    }
203
+
204
+    /**
205
+     * @param $message
206
+     * @param array $additional
207
+     * @return array
208
+     */
209
+    private function error($message, array $additional=array()) {
210
+        return array_merge(array('status' => 'error', 'msg' => $message), $additional);
211
+    }
212
+
213
+    /**
214
+     * @return array
215
+     */
216
+    private function success() {
217
+        return array('status'=>'success');
218
+    }
219
+
220
+    /**
221
+     * @PublicPage
222
+     * @BruteForceProtection(action=passwordResetEmail)
223
+     * @AnonRateThrottle(limit=10, period=300)
224
+     *
225
+     * @param string $user
226
+     * @return JSONResponse
227
+     */
228
+    public function email($user){
229
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
230
+            return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
231
+        }
232
+
233
+        \OCP\Util::emitHook(
234
+            '\OCA\Files_Sharing\API\Server2Server',
235
+            'preLoginNameUsedAsUserName',
236
+            ['uid' => &$user]
237
+        );
238
+
239
+        // FIXME: use HTTP error codes
240
+        try {
241
+            $this->sendEmail($user);
242
+        } catch (\Exception $e){
243
+            $response = new JSONResponse($this->error($e->getMessage()));
244
+            $response->throttle();
245
+            return $response;
246
+        }
247
+
248
+        $response = new JSONResponse($this->success());
249
+        $response->throttle();
250
+        return $response;
251
+    }
252
+
253
+    /**
254
+     * @PublicPage
255
+     * @param string $token
256
+     * @param string $userId
257
+     * @param string $password
258
+     * @param boolean $proceed
259
+     * @return array
260
+     */
261
+    public function setPassword($token, $userId, $password, $proceed) {
262
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
263
+            return $this->error($this->l10n->t('Password reset is disabled'));
264
+        }
265
+
266
+        if ($this->encryptionManager->isEnabled() && !$proceed) {
267
+            $encryptionModules = $this->encryptionManager->getEncryptionModules();
268
+            foreach ($encryptionModules as $module) {
269
+                /** @var IEncryptionModule $instance */
270
+                $instance = call_user_func($module['callback']);
271
+                // this way we can find out whether per-user keys are used or a system wide encryption key
272
+                if ($instance->needDetailedAccessList()) {
273
+                    return $this->error('', array('encryption' => true));
274
+                }
275
+            }
276
+        }
277
+
278
+        try {
279
+            $this->checkPasswordResetToken($token, $userId);
280
+            $user = $this->userManager->get($userId);
281
+
282
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
283
+
284
+            if (!$user->setPassword($password)) {
285
+                throw new \Exception();
286
+            }
287
+
288
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
289
+
290
+            $this->twoFactorManager->clearTwoFactorPending($userId);
291
+
292
+            $this->config->deleteUserValue($userId, 'core', 'lostpassword');
293
+            @\OC::$server->getUserSession()->unsetMagicInCookie();
294
+        } catch (HintException $e){
295
+            return $this->error($e->getHint());
296
+        } catch (\Exception $e){
297
+            return $this->error($e->getMessage());
298
+        }
299
+
300
+        return $this->success();
301
+    }
302
+
303
+    /**
304
+     * @param string $input
305
+     * @throws \Exception
306
+     */
307
+    protected function sendEmail($input) {
308
+        $user = $this->findUserByIdOrMail($input);
309
+        $email = $user->getEMailAddress();
310
+
311
+        if (empty($email)) {
312
+            throw new \Exception(
313
+                $this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
314
+            );
315
+        }
316
+
317
+        // Generate the token. It is stored encrypted in the database with the
318
+        // secret being the users' email address appended with the system secret.
319
+        // This makes the token automatically invalidate once the user changes
320
+        // their email address.
321
+        $token = $this->secureRandom->generate(
322
+            21,
323
+            ISecureRandom::CHAR_DIGITS.
324
+            ISecureRandom::CHAR_LOWER.
325
+            ISecureRandom::CHAR_UPPER
326
+        );
327
+        $tokenValue = $this->timeFactory->getTime() .':'. $token;
328
+        $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
329
+        $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
330
+
331
+        $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
332
+
333
+        $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
334
+            'link' => $link,
335
+        ]);
336
+
337
+        $emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
338
+        $emailTemplate->addHeader();
339
+        $emailTemplate->addHeading($this->l10n->t('Password reset'));
340
+
341
+        $emailTemplate->addBodyText(
342
+            $this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.'),
343
+            $this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
344
+        );
345
+
346
+        $emailTemplate->addBodyButton(
347
+            $this->l10n->t('Reset your password'),
348
+            $link,
349
+            false
350
+        );
351
+        $emailTemplate->addFooter();
352
+
353
+        try {
354
+            $message = $this->mailer->createMessage();
355
+            $message->setTo([$email => $user->getUID()]);
356
+            $message->setFrom([$this->from => $this->defaults->getName()]);
357
+            $message->useTemplate($emailTemplate);
358
+            $this->mailer->send($message);
359
+        } catch (\Exception $e) {
360
+            throw new \Exception($this->l10n->t(
361
+                'Couldn\'t send reset email. Please contact your administrator.'
362
+            ));
363
+        }
364
+    }
365
+
366
+    /**
367
+     * @param string $input
368
+     * @return IUser
369
+     * @throws \InvalidArgumentException
370
+     */
371
+    protected function findUserByIdOrMail($input) {
372
+        $user = $this->userManager->get($input);
373
+        if ($user instanceof IUser) {
374
+            if (!$user->isEnabled()) {
375
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
376
+            }
377
+
378
+            return $user;
379
+        }
380
+        $users = $this->userManager->getByEmail($input);
381
+        if (count($users) === 1) {
382
+            $user = $users[0];
383
+            if (!$user->isEnabled()) {
384
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
385
+            }
386
+
387
+            return $user;
388
+        }
389
+
390
+        throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
391
+    }
392 392
 }
Please login to merge, or discard this patch.