Completed
Push — stable13 ( c99529...4ff6ea )
by Morris
58:12 queued 30:27
created
lib/private/Session/Internal.php 2 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -32,8 +32,6 @@
 block discarded – undo
32 32
 
33 33
 use OC\Authentication\Exceptions\InvalidTokenException;
34 34
 use OC\Authentication\Token\IProvider;
35
-use OC\SystemConfig;
36
-use OCP\IConfig;
37 35
 use OCP\Session\Exceptions\SessionNotAvailableException;
38 36
 
39 37
 /**
Please login to merge, or discard this patch.
Indentation   +166 added lines, -166 removed lines patch added patch discarded remove patch
@@ -43,170 +43,170 @@
 block discarded – undo
43 43
  * @package OC\Session
44 44
  */
45 45
 class Internal extends Session {
46
-	/**
47
-	 * @param string $name
48
-	 * @throws \Exception
49
-	 */
50
-	public function __construct($name) {
51
-		set_error_handler(array($this, 'trapError'));
52
-		$this->invoke('session_name', [$name]);
53
-		try {
54
-			$this->invoke('session_start');
55
-		} catch (\Exception $e) {
56
-			setcookie($this->invoke('session_name'), null, -1, \OC::$WEBROOT ?: '/');
57
-		}
58
-		restore_error_handler();
59
-		if (!isset($_SESSION)) {
60
-			throw new \Exception('Failed to start session');
61
-		}
62
-	}
63
-
64
-	/**
65
-	 * @param string $key
66
-	 * @param integer $value
67
-	 */
68
-	public function set($key, $value) {
69
-		$this->validateSession();
70
-		$_SESSION[$key] = $value;
71
-	}
72
-
73
-	/**
74
-	 * @param string $key
75
-	 * @return mixed
76
-	 */
77
-	public function get($key) {
78
-		if (!$this->exists($key)) {
79
-			return null;
80
-		}
81
-		return $_SESSION[$key];
82
-	}
83
-
84
-	/**
85
-	 * @param string $key
86
-	 * @return bool
87
-	 */
88
-	public function exists($key) {
89
-		return isset($_SESSION[$key]);
90
-	}
91
-
92
-	/**
93
-	 * @param string $key
94
-	 */
95
-	public function remove($key) {
96
-		if (isset($_SESSION[$key])) {
97
-			unset($_SESSION[$key]);
98
-		}
99
-	}
100
-
101
-	public function clear() {
102
-		$this->invoke('session_unset');
103
-		$this->regenerateId();
104
-		$this->invoke('session_start', [], true);
105
-		$_SESSION = [];
106
-	}
107
-
108
-	public function close() {
109
-		$this->invoke('session_write_close');
110
-		parent::close();
111
-	}
112
-
113
-	/**
114
-	 * Wrapper around session_regenerate_id
115
-	 *
116
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
117
-	 * @param bool $updateToken Wheater to update the associated auth token
118
-	 * @return void
119
-	 */
120
-	public function regenerateId($deleteOldSession = true, $updateToken = false) {
121
-		$oldId = null;
122
-
123
-		if ($updateToken) {
124
-			// Get the old id to update the token
125
-			try {
126
-				$oldId = $this->getId();
127
-			} catch (SessionNotAvailableException $e) {
128
-				// We can't update a token if there is no previous id
129
-				$updateToken = false;
130
-			}
131
-		}
132
-
133
-		try {
134
-			@session_regenerate_id($deleteOldSession);
135
-		} catch (\Error $e) {
136
-			$this->trapError($e->getCode(), $e->getMessage());
137
-		}
138
-
139
-		if ($updateToken) {
140
-			// Get the new id to update the token
141
-			$newId = $this->getId();
142
-
143
-			/** @var IProvider $tokenProvider */
144
-			$tokenProvider = \OC::$server->query(IProvider::class);
145
-
146
-			try {
147
-				$tokenProvider->renewSessionToken($oldId, $newId);
148
-			} catch (InvalidTokenException $e) {
149
-				// Just ignore
150
-			}
151
-		}
152
-	}
153
-
154
-	/**
155
-	 * Wrapper around session_id
156
-	 *
157
-	 * @return string
158
-	 * @throws SessionNotAvailableException
159
-	 * @since 9.1.0
160
-	 */
161
-	public function getId() {
162
-		$id = $this->invoke('session_id', [], true);
163
-		if ($id === '') {
164
-			throw new SessionNotAvailableException();
165
-		}
166
-		return $id;
167
-	}
168
-
169
-	/**
170
-	 * @throws \Exception
171
-	 */
172
-	public function reopen() {
173
-		throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
174
-	}
175
-
176
-	/**
177
-	 * @param int $errorNumber
178
-	 * @param string $errorString
179
-	 * @throws \ErrorException
180
-	 */
181
-	public function trapError($errorNumber, $errorString) {
182
-		throw new \ErrorException($errorString);
183
-	}
184
-
185
-	/**
186
-	 * @throws \Exception
187
-	 */
188
-	private function validateSession() {
189
-		if ($this->sessionClosed) {
190
-			throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed');
191
-		}
192
-	}
193
-
194
-	/**
195
-	 * @param string $functionName the full session_* function name
196
-	 * @param array $parameters
197
-	 * @param bool $silence whether to suppress warnings
198
-	 * @throws \ErrorException via trapError
199
-	 * @return mixed
200
-	 */
201
-	private function invoke($functionName, array $parameters = [], $silence = false) {
202
-		try {
203
-			if($silence) {
204
-				return @call_user_func_array($functionName, $parameters);
205
-			} else {
206
-				return call_user_func_array($functionName, $parameters);
207
-			}
208
-		} catch(\Error $e) {
209
-			$this->trapError($e->getCode(), $e->getMessage());
210
-		}
211
-	}
46
+    /**
47
+     * @param string $name
48
+     * @throws \Exception
49
+     */
50
+    public function __construct($name) {
51
+        set_error_handler(array($this, 'trapError'));
52
+        $this->invoke('session_name', [$name]);
53
+        try {
54
+            $this->invoke('session_start');
55
+        } catch (\Exception $e) {
56
+            setcookie($this->invoke('session_name'), null, -1, \OC::$WEBROOT ?: '/');
57
+        }
58
+        restore_error_handler();
59
+        if (!isset($_SESSION)) {
60
+            throw new \Exception('Failed to start session');
61
+        }
62
+    }
63
+
64
+    /**
65
+     * @param string $key
66
+     * @param integer $value
67
+     */
68
+    public function set($key, $value) {
69
+        $this->validateSession();
70
+        $_SESSION[$key] = $value;
71
+    }
72
+
73
+    /**
74
+     * @param string $key
75
+     * @return mixed
76
+     */
77
+    public function get($key) {
78
+        if (!$this->exists($key)) {
79
+            return null;
80
+        }
81
+        return $_SESSION[$key];
82
+    }
83
+
84
+    /**
85
+     * @param string $key
86
+     * @return bool
87
+     */
88
+    public function exists($key) {
89
+        return isset($_SESSION[$key]);
90
+    }
91
+
92
+    /**
93
+     * @param string $key
94
+     */
95
+    public function remove($key) {
96
+        if (isset($_SESSION[$key])) {
97
+            unset($_SESSION[$key]);
98
+        }
99
+    }
100
+
101
+    public function clear() {
102
+        $this->invoke('session_unset');
103
+        $this->regenerateId();
104
+        $this->invoke('session_start', [], true);
105
+        $_SESSION = [];
106
+    }
107
+
108
+    public function close() {
109
+        $this->invoke('session_write_close');
110
+        parent::close();
111
+    }
112
+
113
+    /**
114
+     * Wrapper around session_regenerate_id
115
+     *
116
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
117
+     * @param bool $updateToken Wheater to update the associated auth token
118
+     * @return void
119
+     */
120
+    public function regenerateId($deleteOldSession = true, $updateToken = false) {
121
+        $oldId = null;
122
+
123
+        if ($updateToken) {
124
+            // Get the old id to update the token
125
+            try {
126
+                $oldId = $this->getId();
127
+            } catch (SessionNotAvailableException $e) {
128
+                // We can't update a token if there is no previous id
129
+                $updateToken = false;
130
+            }
131
+        }
132
+
133
+        try {
134
+            @session_regenerate_id($deleteOldSession);
135
+        } catch (\Error $e) {
136
+            $this->trapError($e->getCode(), $e->getMessage());
137
+        }
138
+
139
+        if ($updateToken) {
140
+            // Get the new id to update the token
141
+            $newId = $this->getId();
142
+
143
+            /** @var IProvider $tokenProvider */
144
+            $tokenProvider = \OC::$server->query(IProvider::class);
145
+
146
+            try {
147
+                $tokenProvider->renewSessionToken($oldId, $newId);
148
+            } catch (InvalidTokenException $e) {
149
+                // Just ignore
150
+            }
151
+        }
152
+    }
153
+
154
+    /**
155
+     * Wrapper around session_id
156
+     *
157
+     * @return string
158
+     * @throws SessionNotAvailableException
159
+     * @since 9.1.0
160
+     */
161
+    public function getId() {
162
+        $id = $this->invoke('session_id', [], true);
163
+        if ($id === '') {
164
+            throw new SessionNotAvailableException();
165
+        }
166
+        return $id;
167
+    }
168
+
169
+    /**
170
+     * @throws \Exception
171
+     */
172
+    public function reopen() {
173
+        throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
174
+    }
175
+
176
+    /**
177
+     * @param int $errorNumber
178
+     * @param string $errorString
179
+     * @throws \ErrorException
180
+     */
181
+    public function trapError($errorNumber, $errorString) {
182
+        throw new \ErrorException($errorString);
183
+    }
184
+
185
+    /**
186
+     * @throws \Exception
187
+     */
188
+    private function validateSession() {
189
+        if ($this->sessionClosed) {
190
+            throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed');
191
+        }
192
+    }
193
+
194
+    /**
195
+     * @param string $functionName the full session_* function name
196
+     * @param array $parameters
197
+     * @param bool $silence whether to suppress warnings
198
+     * @throws \ErrorException via trapError
199
+     * @return mixed
200
+     */
201
+    private function invoke($functionName, array $parameters = [], $silence = false) {
202
+        try {
203
+            if($silence) {
204
+                return @call_user_func_array($functionName, $parameters);
205
+            } else {
206
+                return call_user_func_array($functionName, $parameters);
207
+            }
208
+        } catch(\Error $e) {
209
+            $this->trapError($e->getCode(), $e->getMessage());
210
+        }
211
+    }
212 212
 }
Please login to merge, or discard this patch.
lib/private/User/Session.php 1 patch
Indentation   +826 added lines, -826 removed lines patch added patch discarded remove patch
@@ -84,832 +84,832 @@
 block discarded – undo
84 84
  */
85 85
 class Session implements IUserSession, Emitter {
86 86
 
87
-	/** @var Manager|PublicEmitter $manager */
88
-	private $manager;
89
-
90
-	/** @var ISession $session */
91
-	private $session;
92
-
93
-	/** @var ITimeFactory */
94
-	private $timeFactory;
95
-
96
-	/** @var IProvider */
97
-	private $tokenProvider;
98
-
99
-	/** @var IConfig */
100
-	private $config;
101
-
102
-	/** @var User $activeUser */
103
-	protected $activeUser;
104
-
105
-	/** @var ISecureRandom */
106
-	private $random;
107
-
108
-	/** @var ILockdownManager  */
109
-	private $lockdownManager;
110
-
111
-	/** @var ILogger */
112
-	private $logger;
113
-
114
-	/**
115
-	 * @param Manager $manager
116
-	 * @param ISession $session
117
-	 * @param ITimeFactory $timeFactory
118
-	 * @param IProvider $tokenProvider
119
-	 * @param IConfig $config
120
-	 * @param ISecureRandom $random
121
-	 * @param ILockdownManager $lockdownManager
122
-	 * @param ILogger $logger
123
-	 */
124
-	public function __construct(Manager $manager,
125
-								ISession $session,
126
-								ITimeFactory $timeFactory,
127
-								$tokenProvider,
128
-								IConfig $config,
129
-								ISecureRandom $random,
130
-								ILockdownManager $lockdownManager,
131
-								ILogger $logger) {
132
-		$this->manager = $manager;
133
-		$this->session = $session;
134
-		$this->timeFactory = $timeFactory;
135
-		$this->tokenProvider = $tokenProvider;
136
-		$this->config = $config;
137
-		$this->random = $random;
138
-		$this->lockdownManager = $lockdownManager;
139
-		$this->logger = $logger;
140
-	}
141
-
142
-	/**
143
-	 * @param IProvider $provider
144
-	 */
145
-	public function setTokenProvider(IProvider $provider) {
146
-		$this->tokenProvider = $provider;
147
-	}
148
-
149
-	/**
150
-	 * @param string $scope
151
-	 * @param string $method
152
-	 * @param callable $callback
153
-	 */
154
-	public function listen($scope, $method, callable $callback) {
155
-		$this->manager->listen($scope, $method, $callback);
156
-	}
157
-
158
-	/**
159
-	 * @param string $scope optional
160
-	 * @param string $method optional
161
-	 * @param callable $callback optional
162
-	 */
163
-	public function removeListener($scope = null, $method = null, callable $callback = null) {
164
-		$this->manager->removeListener($scope, $method, $callback);
165
-	}
166
-
167
-	/**
168
-	 * get the manager object
169
-	 *
170
-	 * @return Manager|PublicEmitter
171
-	 */
172
-	public function getManager() {
173
-		return $this->manager;
174
-	}
175
-
176
-	/**
177
-	 * get the session object
178
-	 *
179
-	 * @return ISession
180
-	 */
181
-	public function getSession() {
182
-		return $this->session;
183
-	}
184
-
185
-	/**
186
-	 * set the session object
187
-	 *
188
-	 * @param ISession $session
189
-	 */
190
-	public function setSession(ISession $session) {
191
-		if ($this->session instanceof ISession) {
192
-			$this->session->close();
193
-		}
194
-		$this->session = $session;
195
-		$this->activeUser = null;
196
-	}
197
-
198
-	/**
199
-	 * set the currently active user
200
-	 *
201
-	 * @param IUser|null $user
202
-	 */
203
-	public function setUser($user) {
204
-		if (is_null($user)) {
205
-			$this->session->remove('user_id');
206
-		} else {
207
-			$this->session->set('user_id', $user->getUID());
208
-		}
209
-		$this->activeUser = $user;
210
-	}
211
-
212
-	/**
213
-	 * get the current active user
214
-	 *
215
-	 * @return IUser|null Current user, otherwise null
216
-	 */
217
-	public function getUser() {
218
-		// FIXME: This is a quick'n dirty work-around for the incognito mode as
219
-		// described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
220
-		if (OC_User::isIncognitoMode()) {
221
-			return null;
222
-		}
223
-		if (is_null($this->activeUser)) {
224
-			$uid = $this->session->get('user_id');
225
-			if (is_null($uid)) {
226
-				return null;
227
-			}
228
-			$this->activeUser = $this->manager->get($uid);
229
-			if (is_null($this->activeUser)) {
230
-				return null;
231
-			}
232
-			$this->validateSession();
233
-		}
234
-		return $this->activeUser;
235
-	}
236
-
237
-	/**
238
-	 * Validate whether the current session is valid
239
-	 *
240
-	 * - For token-authenticated clients, the token validity is checked
241
-	 * - For browsers, the session token validity is checked
242
-	 */
243
-	protected function validateSession() {
244
-		$token = null;
245
-		$appPassword = $this->session->get('app_password');
246
-
247
-		if (is_null($appPassword)) {
248
-			try {
249
-				$token = $this->session->getId();
250
-			} catch (SessionNotAvailableException $ex) {
251
-				return;
252
-			}
253
-		} else {
254
-			$token = $appPassword;
255
-		}
256
-
257
-		if (!$this->validateToken($token)) {
258
-			// Session was invalidated
259
-			$this->logout();
260
-		}
261
-	}
262
-
263
-	/**
264
-	 * Checks whether the user is logged in
265
-	 *
266
-	 * @return bool if logged in
267
-	 */
268
-	public function isLoggedIn() {
269
-		$user = $this->getUser();
270
-		if (is_null($user)) {
271
-			return false;
272
-		}
273
-
274
-		return $user->isEnabled();
275
-	}
276
-
277
-	/**
278
-	 * set the login name
279
-	 *
280
-	 * @param string|null $loginName for the logged in user
281
-	 */
282
-	public function setLoginName($loginName) {
283
-		if (is_null($loginName)) {
284
-			$this->session->remove('loginname');
285
-		} else {
286
-			$this->session->set('loginname', $loginName);
287
-		}
288
-	}
289
-
290
-	/**
291
-	 * get the login name of the current user
292
-	 *
293
-	 * @return string
294
-	 */
295
-	public function getLoginName() {
296
-		if ($this->activeUser) {
297
-			return $this->session->get('loginname');
298
-		} else {
299
-			$uid = $this->session->get('user_id');
300
-			if ($uid) {
301
-				$this->activeUser = $this->manager->get($uid);
302
-				return $this->session->get('loginname');
303
-			} else {
304
-				return null;
305
-			}
306
-		}
307
-	}
308
-
309
-	/**
310
-	 * set the token id
311
-	 *
312
-	 * @param int|null $token that was used to log in
313
-	 */
314
-	protected function setToken($token) {
315
-		if ($token === null) {
316
-			$this->session->remove('token-id');
317
-		} else {
318
-			$this->session->set('token-id', $token);
319
-		}
320
-	}
321
-
322
-	/**
323
-	 * try to log in with the provided credentials
324
-	 *
325
-	 * @param string $uid
326
-	 * @param string $password
327
-	 * @return boolean|null
328
-	 * @throws LoginException
329
-	 */
330
-	public function login($uid, $password) {
331
-		$this->session->regenerateId();
332
-		if ($this->validateToken($password, $uid)) {
333
-			return $this->loginWithToken($password);
334
-		}
335
-		return $this->loginWithPassword($uid, $password);
336
-	}
337
-
338
-	/**
339
-	 * @param IUser $user
340
-	 * @param array $loginDetails
341
-	 * @param bool $regenerateSessionId
342
-	 * @return true returns true if login successful or an exception otherwise
343
-	 * @throws LoginException
344
-	 */
345
-	public function completeLogin(IUser $user, array $loginDetails, $regenerateSessionId = true) {
346
-		if (!$user->isEnabled()) {
347
-			// disabled users can not log in
348
-			// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
349
-			$message = \OC::$server->getL10N('lib')->t('User disabled');
350
-			throw new LoginException($message);
351
-		}
352
-
353
-		if($regenerateSessionId) {
354
-			$this->session->regenerateId();
355
-		}
356
-
357
-		$this->setUser($user);
358
-		$this->setLoginName($loginDetails['loginName']);
359
-
360
-		if(isset($loginDetails['token']) && $loginDetails['token'] instanceof IToken) {
361
-			$this->setToken($loginDetails['token']->getId());
362
-			$this->lockdownManager->setToken($loginDetails['token']);
363
-			$firstTimeLogin = false;
364
-		} else {
365
-			$this->setToken(null);
366
-			$firstTimeLogin = $user->updateLastLoginTimestamp();
367
-		}
368
-		$this->manager->emit('\OC\User', 'postLogin', [$user, $loginDetails['password']]);
369
-		if($this->isLoggedIn()) {
370
-			$this->prepareUserLogin($firstTimeLogin, $regenerateSessionId);
371
-			return true;
372
-		} else {
373
-			$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
374
-			throw new LoginException($message);
375
-		}
376
-	}
377
-
378
-	/**
379
-	 * Tries to log in a client
380
-	 *
381
-	 * Checks token auth enforced
382
-	 * Checks 2FA enabled
383
-	 *
384
-	 * @param string $user
385
-	 * @param string $password
386
-	 * @param IRequest $request
387
-	 * @param OC\Security\Bruteforce\Throttler $throttler
388
-	 * @throws LoginException
389
-	 * @throws PasswordLoginForbiddenException
390
-	 * @return boolean
391
-	 */
392
-	public function logClientIn($user,
393
-								$password,
394
-								IRequest $request,
395
-								OC\Security\Bruteforce\Throttler $throttler) {
396
-		$currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
397
-
398
-		if ($this->manager instanceof PublicEmitter) {
399
-			$this->manager->emit('\OC\User', 'preLogin', array($user, $password));
400
-		}
401
-
402
-		$isTokenPassword = $this->isTokenPassword($password);
403
-		if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
404
-			throw new PasswordLoginForbiddenException();
405
-		}
406
-		if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
407
-			throw new PasswordLoginForbiddenException();
408
-		}
409
-
410
-		// Try to login with this username and password
411
-		if (!$this->login($user, $password) ) {
412
-
413
-			// Failed, maybe the user used their email address
414
-			$users = $this->manager->getByEmail($user);
415
-			if (!(\count($users) === 1 && $this->login($users[0]->getUID(), $password))) {
416
-
417
-				$this->logger->warning('Login failed: \'' . $user . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
418
-
419
-				$throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
420
-				if ($currentDelay === 0) {
421
-					$throttler->sleepDelay($request->getRemoteAddress(), 'login');
422
-				}
423
-				return false;
424
-			}
425
-		}
426
-
427
-		if ($isTokenPassword) {
428
-			$this->session->set('app_password', $password);
429
-		} else if($this->supportsCookies($request)) {
430
-			// Password login, but cookies supported -> create (browser) session token
431
-			$this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
432
-		}
433
-
434
-		return true;
435
-	}
436
-
437
-	protected function supportsCookies(IRequest $request) {
438
-		if (!is_null($request->getCookie('cookie_test'))) {
439
-			return true;
440
-		}
441
-		setcookie('cookie_test', 'test', $this->timeFactory->getTime() + 3600);
442
-		return false;
443
-	}
444
-
445
-	private function isTokenAuthEnforced() {
446
-		return $this->config->getSystemValue('token_auth_enforced', false);
447
-	}
448
-
449
-	protected function isTwoFactorEnforced($username) {
450
-		Util::emitHook(
451
-			'\OCA\Files_Sharing\API\Server2Server',
452
-			'preLoginNameUsedAsUserName',
453
-			array('uid' => &$username)
454
-		);
455
-		$user = $this->manager->get($username);
456
-		if (is_null($user)) {
457
-			$users = $this->manager->getByEmail($username);
458
-			if (empty($users)) {
459
-				return false;
460
-			}
461
-			if (count($users) !== 1) {
462
-				return true;
463
-			}
464
-			$user = $users[0];
465
-		}
466
-		// DI not possible due to cyclic dependencies :'-/
467
-		return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
468
-	}
469
-
470
-	/**
471
-	 * Check if the given 'password' is actually a device token
472
-	 *
473
-	 * @param string $password
474
-	 * @return boolean
475
-	 */
476
-	public function isTokenPassword($password) {
477
-		try {
478
-			$this->tokenProvider->getToken($password);
479
-			return true;
480
-		} catch (InvalidTokenException $ex) {
481
-			return false;
482
-		}
483
-	}
484
-
485
-	protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) {
486
-		if ($refreshCsrfToken) {
487
-			// TODO: mock/inject/use non-static
488
-			// Refresh the token
489
-			\OC::$server->getCsrfTokenManager()->refreshToken();
490
-		}
491
-
492
-		//we need to pass the user name, which may differ from login name
493
-		$user = $this->getUser()->getUID();
494
-		OC_Util::setupFS($user);
495
-
496
-		if ($firstTimeLogin) {
497
-			// TODO: lock necessary?
498
-			//trigger creation of user home and /files folder
499
-			$userFolder = \OC::$server->getUserFolder($user);
500
-
501
-			try {
502
-				// copy skeleton
503
-				\OC_Util::copySkeleton($user, $userFolder);
504
-			} catch (NotPermittedException $ex) {
505
-				// read only uses
506
-			}
507
-
508
-			// trigger any other initialization
509
-			\OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
510
-		}
511
-	}
512
-
513
-	/**
514
-	 * Tries to login the user with HTTP Basic Authentication
515
-	 *
516
-	 * @todo do not allow basic auth if the user is 2FA enforced
517
-	 * @param IRequest $request
518
-	 * @param OC\Security\Bruteforce\Throttler $throttler
519
-	 * @return boolean if the login was successful
520
-	 */
521
-	public function tryBasicAuthLogin(IRequest $request,
522
-									  OC\Security\Bruteforce\Throttler $throttler) {
523
-		if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
524
-			try {
525
-				if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
526
-					/**
527
-					 * Add DAV authenticated. This should in an ideal world not be
528
-					 * necessary but the iOS App reads cookies from anywhere instead
529
-					 * only the DAV endpoint.
530
-					 * This makes sure that the cookies will be valid for the whole scope
531
-					 * @see https://github.com/owncloud/core/issues/22893
532
-					 */
533
-					$this->session->set(
534
-						Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
535
-					);
536
-
537
-					// Set the last-password-confirm session to make the sudo mode work
538
-					 $this->session->set('last-password-confirm', $this->timeFactory->getTime());
539
-
540
-					return true;
541
-				}
542
-			} catch (PasswordLoginForbiddenException $ex) {
543
-				// Nothing to do
544
-			}
545
-		}
546
-		return false;
547
-	}
548
-
549
-	/**
550
-	 * Log an user in via login name and password
551
-	 *
552
-	 * @param string $uid
553
-	 * @param string $password
554
-	 * @return boolean
555
-	 * @throws LoginException if an app canceld the login process or the user is not enabled
556
-	 */
557
-	private function loginWithPassword($uid, $password) {
558
-		$user = $this->manager->checkPasswordNoLogging($uid, $password);
559
-		if ($user === false) {
560
-			// Password check failed
561
-			return false;
562
-		}
563
-
564
-		return $this->completeLogin($user, ['loginName' => $uid, 'password' => $password], false);
565
-	}
566
-
567
-	/**
568
-	 * Log an user in with a given token (id)
569
-	 *
570
-	 * @param string $token
571
-	 * @return boolean
572
-	 * @throws LoginException if an app canceled the login process or the user is not enabled
573
-	 */
574
-	private function loginWithToken($token) {
575
-		try {
576
-			$dbToken = $this->tokenProvider->getToken($token);
577
-		} catch (InvalidTokenException $ex) {
578
-			return false;
579
-		}
580
-		$uid = $dbToken->getUID();
581
-
582
-		// When logging in with token, the password must be decrypted first before passing to login hook
583
-		$password = '';
584
-		try {
585
-			$password = $this->tokenProvider->getPassword($dbToken, $token);
586
-		} catch (PasswordlessTokenException $ex) {
587
-			// Ignore and use empty string instead
588
-		}
589
-
590
-		$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
591
-
592
-		$user = $this->manager->get($uid);
593
-		if (is_null($user)) {
594
-			// user does not exist
595
-			return false;
596
-		}
597
-
598
-		return $this->completeLogin(
599
-			$user,
600
-			[
601
-				'loginName' => $dbToken->getLoginName(),
602
-				'password' => $password,
603
-				'token' => $dbToken
604
-			],
605
-			false);
606
-	}
607
-
608
-	/**
609
-	 * Create a new session token for the given user credentials
610
-	 *
611
-	 * @param IRequest $request
612
-	 * @param string $uid user UID
613
-	 * @param string $loginName login name
614
-	 * @param string $password
615
-	 * @param int $remember
616
-	 * @return boolean
617
-	 */
618
-	public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
619
-		if (is_null($this->manager->get($uid))) {
620
-			// User does not exist
621
-			return false;
622
-		}
623
-		$name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
624
-		try {
625
-			$sessionId = $this->session->getId();
626
-			$pwd = $this->getPassword($password);
627
-			// Make sure the current sessionId has no leftover tokens
628
-			$this->tokenProvider->invalidateToken($sessionId);
629
-			$this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
630
-			return true;
631
-		} catch (SessionNotAvailableException $ex) {
632
-			// This can happen with OCC, where a memory session is used
633
-			// if a memory session is used, we shouldn't create a session token anyway
634
-			return false;
635
-		}
636
-	}
637
-
638
-	/**
639
-	 * Checks if the given password is a token.
640
-	 * If yes, the password is extracted from the token.
641
-	 * If no, the same password is returned.
642
-	 *
643
-	 * @param string $password either the login password or a device token
644
-	 * @return string|null the password or null if none was set in the token
645
-	 */
646
-	private function getPassword($password) {
647
-		if (is_null($password)) {
648
-			// This is surely no token ;-)
649
-			return null;
650
-		}
651
-		try {
652
-			$token = $this->tokenProvider->getToken($password);
653
-			try {
654
-				return $this->tokenProvider->getPassword($token, $password);
655
-			} catch (PasswordlessTokenException $ex) {
656
-				return null;
657
-			}
658
-		} catch (InvalidTokenException $ex) {
659
-			return $password;
660
-		}
661
-	}
662
-
663
-	/**
664
-	 * @param IToken $dbToken
665
-	 * @param string $token
666
-	 * @return boolean
667
-	 */
668
-	private function checkTokenCredentials(IToken $dbToken, $token) {
669
-		// Check whether login credentials are still valid and the user was not disabled
670
-		// This check is performed each 5 minutes
671
-		$lastCheck = $dbToken->getLastCheck() ? : 0;
672
-		$now = $this->timeFactory->getTime();
673
-		if ($lastCheck > ($now - 60 * 5)) {
674
-			// Checked performed recently, nothing to do now
675
-			return true;
676
-		}
677
-
678
-		try {
679
-			$pwd = $this->tokenProvider->getPassword($dbToken, $token);
680
-		} catch (InvalidTokenException $ex) {
681
-			// An invalid token password was used -> log user out
682
-			return false;
683
-		} catch (PasswordlessTokenException $ex) {
684
-			// Token has no password
685
-
686
-			if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
687
-				$this->tokenProvider->invalidateToken($token);
688
-				return false;
689
-			}
690
-
691
-			$dbToken->setLastCheck($now);
692
-			return true;
693
-		}
694
-
695
-		if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
696
-			|| (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
697
-			$this->tokenProvider->invalidateToken($token);
698
-			// Password has changed or user was disabled -> log user out
699
-			return false;
700
-		}
701
-		$dbToken->setLastCheck($now);
702
-		return true;
703
-	}
704
-
705
-	/**
706
-	 * Check if the given token exists and performs password/user-enabled checks
707
-	 *
708
-	 * Invalidates the token if checks fail
709
-	 *
710
-	 * @param string $token
711
-	 * @param string $user login name
712
-	 * @return boolean
713
-	 */
714
-	private function validateToken($token, $user = null) {
715
-		try {
716
-			$dbToken = $this->tokenProvider->getToken($token);
717
-		} catch (InvalidTokenException $ex) {
718
-			return false;
719
-		}
720
-
721
-		// Check if login names match
722
-		if (!is_null($user) && $dbToken->getLoginName() !== $user) {
723
-			// TODO: this makes it imposssible to use different login names on browser and client
724
-			// e.g. login by e-mail '[email protected]' on browser for generating the token will not
725
-			//      allow to use the client token with the login name 'user'.
726
-			return false;
727
-		}
728
-
729
-		if (!$this->checkTokenCredentials($dbToken, $token)) {
730
-			return false;
731
-		}
732
-
733
-		$this->tokenProvider->updateTokenActivity($dbToken);
734
-
735
-		return true;
736
-	}
737
-
738
-	/**
739
-	 * Tries to login the user with auth token header
740
-	 *
741
-	 * @param IRequest $request
742
-	 * @todo check remember me cookie
743
-	 * @return boolean
744
-	 */
745
-	public function tryTokenLogin(IRequest $request) {
746
-		$authHeader = $request->getHeader('Authorization');
747
-		if (strpos($authHeader, 'Bearer ') === false) {
748
-			// No auth header, let's try session id
749
-			try {
750
-				$token = $this->session->getId();
751
-			} catch (SessionNotAvailableException $ex) {
752
-				return false;
753
-			}
754
-		} else {
755
-			$token = substr($authHeader, 7);
756
-		}
757
-
758
-		if (!$this->loginWithToken($token)) {
759
-			return false;
760
-		}
761
-		if(!$this->validateToken($token)) {
762
-			return false;
763
-		}
764
-		return true;
765
-	}
766
-
767
-	/**
768
-	 * perform login using the magic cookie (remember login)
769
-	 *
770
-	 * @param string $uid the username
771
-	 * @param string $currentToken
772
-	 * @param string $oldSessionId
773
-	 * @return bool
774
-	 */
775
-	public function loginWithCookie($uid, $currentToken, $oldSessionId) {
776
-		$this->session->regenerateId();
777
-		$this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
778
-		$user = $this->manager->get($uid);
779
-		if (is_null($user)) {
780
-			// user does not exist
781
-			return false;
782
-		}
783
-
784
-		// get stored tokens
785
-		$tokens = $this->config->getUserKeys($uid, 'login_token');
786
-		// test cookies token against stored tokens
787
-		if (!in_array($currentToken, $tokens, true)) {
788
-			return false;
789
-		}
790
-		// replace successfully used token with a new one
791
-		$this->config->deleteUserValue($uid, 'login_token', $currentToken);
792
-		$newToken = $this->random->generate(32);
793
-		$this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime());
794
-
795
-		try {
796
-			$sessionId = $this->session->getId();
797
-			$this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
798
-		} catch (SessionNotAvailableException $ex) {
799
-			return false;
800
-		} catch (InvalidTokenException $ex) {
801
-			\OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
802
-			return false;
803
-		}
804
-
805
-		$this->setMagicInCookie($user->getUID(), $newToken);
806
-		$token = $this->tokenProvider->getToken($sessionId);
807
-
808
-		//login
809
-		$this->setUser($user);
810
-		$this->setLoginName($token->getLoginName());
811
-		$this->setToken($token->getId());
812
-		$this->lockdownManager->setToken($token);
813
-		$user->updateLastLoginTimestamp();
814
-		$password = null;
815
-		try {
816
-			$password = $this->tokenProvider->getPassword($token, $sessionId);
817
-		} catch (PasswordlessTokenException $ex) {
818
-			// Ignore
819
-		}
820
-		$this->manager->emit('\OC\User', 'postRememberedLogin', [$user, $password]);
821
-		return true;
822
-	}
823
-
824
-	/**
825
-	 * @param IUser $user
826
-	 */
827
-	public function createRememberMeToken(IUser $user) {
828
-		$token = $this->random->generate(32);
829
-		$this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime());
830
-		$this->setMagicInCookie($user->getUID(), $token);
831
-	}
832
-
833
-	/**
834
-	 * logout the user from the session
835
-	 */
836
-	public function logout() {
837
-		$this->manager->emit('\OC\User', 'logout');
838
-		$user = $this->getUser();
839
-		if (!is_null($user)) {
840
-			try {
841
-				$this->tokenProvider->invalidateToken($this->session->getId());
842
-			} catch (SessionNotAvailableException $ex) {
843
-
844
-			}
845
-		}
846
-		$this->setUser(null);
847
-		$this->setLoginName(null);
848
-		$this->setToken(null);
849
-		$this->unsetMagicInCookie();
850
-		$this->session->clear();
851
-		$this->manager->emit('\OC\User', 'postLogout');
852
-	}
853
-
854
-	/**
855
-	 * Set cookie value to use in next page load
856
-	 *
857
-	 * @param string $username username to be set
858
-	 * @param string $token
859
-	 */
860
-	public function setMagicInCookie($username, $token) {
861
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
862
-		$webRoot = \OC::$WEBROOT;
863
-		if ($webRoot === '') {
864
-			$webRoot = '/';
865
-		}
866
-
867
-		$expires = $this->timeFactory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
868
-		setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
869
-		setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
870
-		try {
871
-			setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
872
-		} catch (SessionNotAvailableException $ex) {
873
-			// ignore
874
-		}
875
-	}
876
-
877
-	/**
878
-	 * Remove cookie for "remember username"
879
-	 */
880
-	public function unsetMagicInCookie() {
881
-		//TODO: DI for cookies and IRequest
882
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
883
-
884
-		unset($_COOKIE['nc_username']); //TODO: DI
885
-		unset($_COOKIE['nc_token']);
886
-		unset($_COOKIE['nc_session_id']);
887
-		setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
888
-		setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
889
-		setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
890
-		// old cookies might be stored under /webroot/ instead of /webroot
891
-		// and Firefox doesn't like it!
892
-		setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
893
-		setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
894
-		setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
895
-	}
896
-
897
-	/**
898
-	 * Update password of the browser session token if there is one
899
-	 *
900
-	 * @param string $password
901
-	 */
902
-	public function updateSessionTokenPassword($password) {
903
-		try {
904
-			$sessionId = $this->session->getId();
905
-			$token = $this->tokenProvider->getToken($sessionId);
906
-			$this->tokenProvider->setPassword($token, $sessionId, $password);
907
-		} catch (SessionNotAvailableException $ex) {
908
-			// Nothing to do
909
-		} catch (InvalidTokenException $ex) {
910
-			// Nothing to do
911
-		}
912
-	}
87
+    /** @var Manager|PublicEmitter $manager */
88
+    private $manager;
89
+
90
+    /** @var ISession $session */
91
+    private $session;
92
+
93
+    /** @var ITimeFactory */
94
+    private $timeFactory;
95
+
96
+    /** @var IProvider */
97
+    private $tokenProvider;
98
+
99
+    /** @var IConfig */
100
+    private $config;
101
+
102
+    /** @var User $activeUser */
103
+    protected $activeUser;
104
+
105
+    /** @var ISecureRandom */
106
+    private $random;
107
+
108
+    /** @var ILockdownManager  */
109
+    private $lockdownManager;
110
+
111
+    /** @var ILogger */
112
+    private $logger;
113
+
114
+    /**
115
+     * @param Manager $manager
116
+     * @param ISession $session
117
+     * @param ITimeFactory $timeFactory
118
+     * @param IProvider $tokenProvider
119
+     * @param IConfig $config
120
+     * @param ISecureRandom $random
121
+     * @param ILockdownManager $lockdownManager
122
+     * @param ILogger $logger
123
+     */
124
+    public function __construct(Manager $manager,
125
+                                ISession $session,
126
+                                ITimeFactory $timeFactory,
127
+                                $tokenProvider,
128
+                                IConfig $config,
129
+                                ISecureRandom $random,
130
+                                ILockdownManager $lockdownManager,
131
+                                ILogger $logger) {
132
+        $this->manager = $manager;
133
+        $this->session = $session;
134
+        $this->timeFactory = $timeFactory;
135
+        $this->tokenProvider = $tokenProvider;
136
+        $this->config = $config;
137
+        $this->random = $random;
138
+        $this->lockdownManager = $lockdownManager;
139
+        $this->logger = $logger;
140
+    }
141
+
142
+    /**
143
+     * @param IProvider $provider
144
+     */
145
+    public function setTokenProvider(IProvider $provider) {
146
+        $this->tokenProvider = $provider;
147
+    }
148
+
149
+    /**
150
+     * @param string $scope
151
+     * @param string $method
152
+     * @param callable $callback
153
+     */
154
+    public function listen($scope, $method, callable $callback) {
155
+        $this->manager->listen($scope, $method, $callback);
156
+    }
157
+
158
+    /**
159
+     * @param string $scope optional
160
+     * @param string $method optional
161
+     * @param callable $callback optional
162
+     */
163
+    public function removeListener($scope = null, $method = null, callable $callback = null) {
164
+        $this->manager->removeListener($scope, $method, $callback);
165
+    }
166
+
167
+    /**
168
+     * get the manager object
169
+     *
170
+     * @return Manager|PublicEmitter
171
+     */
172
+    public function getManager() {
173
+        return $this->manager;
174
+    }
175
+
176
+    /**
177
+     * get the session object
178
+     *
179
+     * @return ISession
180
+     */
181
+    public function getSession() {
182
+        return $this->session;
183
+    }
184
+
185
+    /**
186
+     * set the session object
187
+     *
188
+     * @param ISession $session
189
+     */
190
+    public function setSession(ISession $session) {
191
+        if ($this->session instanceof ISession) {
192
+            $this->session->close();
193
+        }
194
+        $this->session = $session;
195
+        $this->activeUser = null;
196
+    }
197
+
198
+    /**
199
+     * set the currently active user
200
+     *
201
+     * @param IUser|null $user
202
+     */
203
+    public function setUser($user) {
204
+        if (is_null($user)) {
205
+            $this->session->remove('user_id');
206
+        } else {
207
+            $this->session->set('user_id', $user->getUID());
208
+        }
209
+        $this->activeUser = $user;
210
+    }
211
+
212
+    /**
213
+     * get the current active user
214
+     *
215
+     * @return IUser|null Current user, otherwise null
216
+     */
217
+    public function getUser() {
218
+        // FIXME: This is a quick'n dirty work-around for the incognito mode as
219
+        // described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
220
+        if (OC_User::isIncognitoMode()) {
221
+            return null;
222
+        }
223
+        if (is_null($this->activeUser)) {
224
+            $uid = $this->session->get('user_id');
225
+            if (is_null($uid)) {
226
+                return null;
227
+            }
228
+            $this->activeUser = $this->manager->get($uid);
229
+            if (is_null($this->activeUser)) {
230
+                return null;
231
+            }
232
+            $this->validateSession();
233
+        }
234
+        return $this->activeUser;
235
+    }
236
+
237
+    /**
238
+     * Validate whether the current session is valid
239
+     *
240
+     * - For token-authenticated clients, the token validity is checked
241
+     * - For browsers, the session token validity is checked
242
+     */
243
+    protected function validateSession() {
244
+        $token = null;
245
+        $appPassword = $this->session->get('app_password');
246
+
247
+        if (is_null($appPassword)) {
248
+            try {
249
+                $token = $this->session->getId();
250
+            } catch (SessionNotAvailableException $ex) {
251
+                return;
252
+            }
253
+        } else {
254
+            $token = $appPassword;
255
+        }
256
+
257
+        if (!$this->validateToken($token)) {
258
+            // Session was invalidated
259
+            $this->logout();
260
+        }
261
+    }
262
+
263
+    /**
264
+     * Checks whether the user is logged in
265
+     *
266
+     * @return bool if logged in
267
+     */
268
+    public function isLoggedIn() {
269
+        $user = $this->getUser();
270
+        if (is_null($user)) {
271
+            return false;
272
+        }
273
+
274
+        return $user->isEnabled();
275
+    }
276
+
277
+    /**
278
+     * set the login name
279
+     *
280
+     * @param string|null $loginName for the logged in user
281
+     */
282
+    public function setLoginName($loginName) {
283
+        if (is_null($loginName)) {
284
+            $this->session->remove('loginname');
285
+        } else {
286
+            $this->session->set('loginname', $loginName);
287
+        }
288
+    }
289
+
290
+    /**
291
+     * get the login name of the current user
292
+     *
293
+     * @return string
294
+     */
295
+    public function getLoginName() {
296
+        if ($this->activeUser) {
297
+            return $this->session->get('loginname');
298
+        } else {
299
+            $uid = $this->session->get('user_id');
300
+            if ($uid) {
301
+                $this->activeUser = $this->manager->get($uid);
302
+                return $this->session->get('loginname');
303
+            } else {
304
+                return null;
305
+            }
306
+        }
307
+    }
308
+
309
+    /**
310
+     * set the token id
311
+     *
312
+     * @param int|null $token that was used to log in
313
+     */
314
+    protected function setToken($token) {
315
+        if ($token === null) {
316
+            $this->session->remove('token-id');
317
+        } else {
318
+            $this->session->set('token-id', $token);
319
+        }
320
+    }
321
+
322
+    /**
323
+     * try to log in with the provided credentials
324
+     *
325
+     * @param string $uid
326
+     * @param string $password
327
+     * @return boolean|null
328
+     * @throws LoginException
329
+     */
330
+    public function login($uid, $password) {
331
+        $this->session->regenerateId();
332
+        if ($this->validateToken($password, $uid)) {
333
+            return $this->loginWithToken($password);
334
+        }
335
+        return $this->loginWithPassword($uid, $password);
336
+    }
337
+
338
+    /**
339
+     * @param IUser $user
340
+     * @param array $loginDetails
341
+     * @param bool $regenerateSessionId
342
+     * @return true returns true if login successful or an exception otherwise
343
+     * @throws LoginException
344
+     */
345
+    public function completeLogin(IUser $user, array $loginDetails, $regenerateSessionId = true) {
346
+        if (!$user->isEnabled()) {
347
+            // disabled users can not log in
348
+            // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
349
+            $message = \OC::$server->getL10N('lib')->t('User disabled');
350
+            throw new LoginException($message);
351
+        }
352
+
353
+        if($regenerateSessionId) {
354
+            $this->session->regenerateId();
355
+        }
356
+
357
+        $this->setUser($user);
358
+        $this->setLoginName($loginDetails['loginName']);
359
+
360
+        if(isset($loginDetails['token']) && $loginDetails['token'] instanceof IToken) {
361
+            $this->setToken($loginDetails['token']->getId());
362
+            $this->lockdownManager->setToken($loginDetails['token']);
363
+            $firstTimeLogin = false;
364
+        } else {
365
+            $this->setToken(null);
366
+            $firstTimeLogin = $user->updateLastLoginTimestamp();
367
+        }
368
+        $this->manager->emit('\OC\User', 'postLogin', [$user, $loginDetails['password']]);
369
+        if($this->isLoggedIn()) {
370
+            $this->prepareUserLogin($firstTimeLogin, $regenerateSessionId);
371
+            return true;
372
+        } else {
373
+            $message = \OC::$server->getL10N('lib')->t('Login canceled by app');
374
+            throw new LoginException($message);
375
+        }
376
+    }
377
+
378
+    /**
379
+     * Tries to log in a client
380
+     *
381
+     * Checks token auth enforced
382
+     * Checks 2FA enabled
383
+     *
384
+     * @param string $user
385
+     * @param string $password
386
+     * @param IRequest $request
387
+     * @param OC\Security\Bruteforce\Throttler $throttler
388
+     * @throws LoginException
389
+     * @throws PasswordLoginForbiddenException
390
+     * @return boolean
391
+     */
392
+    public function logClientIn($user,
393
+                                $password,
394
+                                IRequest $request,
395
+                                OC\Security\Bruteforce\Throttler $throttler) {
396
+        $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
397
+
398
+        if ($this->manager instanceof PublicEmitter) {
399
+            $this->manager->emit('\OC\User', 'preLogin', array($user, $password));
400
+        }
401
+
402
+        $isTokenPassword = $this->isTokenPassword($password);
403
+        if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
404
+            throw new PasswordLoginForbiddenException();
405
+        }
406
+        if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
407
+            throw new PasswordLoginForbiddenException();
408
+        }
409
+
410
+        // Try to login with this username and password
411
+        if (!$this->login($user, $password) ) {
412
+
413
+            // Failed, maybe the user used their email address
414
+            $users = $this->manager->getByEmail($user);
415
+            if (!(\count($users) === 1 && $this->login($users[0]->getUID(), $password))) {
416
+
417
+                $this->logger->warning('Login failed: \'' . $user . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
418
+
419
+                $throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
420
+                if ($currentDelay === 0) {
421
+                    $throttler->sleepDelay($request->getRemoteAddress(), 'login');
422
+                }
423
+                return false;
424
+            }
425
+        }
426
+
427
+        if ($isTokenPassword) {
428
+            $this->session->set('app_password', $password);
429
+        } else if($this->supportsCookies($request)) {
430
+            // Password login, but cookies supported -> create (browser) session token
431
+            $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
432
+        }
433
+
434
+        return true;
435
+    }
436
+
437
+    protected function supportsCookies(IRequest $request) {
438
+        if (!is_null($request->getCookie('cookie_test'))) {
439
+            return true;
440
+        }
441
+        setcookie('cookie_test', 'test', $this->timeFactory->getTime() + 3600);
442
+        return false;
443
+    }
444
+
445
+    private function isTokenAuthEnforced() {
446
+        return $this->config->getSystemValue('token_auth_enforced', false);
447
+    }
448
+
449
+    protected function isTwoFactorEnforced($username) {
450
+        Util::emitHook(
451
+            '\OCA\Files_Sharing\API\Server2Server',
452
+            'preLoginNameUsedAsUserName',
453
+            array('uid' => &$username)
454
+        );
455
+        $user = $this->manager->get($username);
456
+        if (is_null($user)) {
457
+            $users = $this->manager->getByEmail($username);
458
+            if (empty($users)) {
459
+                return false;
460
+            }
461
+            if (count($users) !== 1) {
462
+                return true;
463
+            }
464
+            $user = $users[0];
465
+        }
466
+        // DI not possible due to cyclic dependencies :'-/
467
+        return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
468
+    }
469
+
470
+    /**
471
+     * Check if the given 'password' is actually a device token
472
+     *
473
+     * @param string $password
474
+     * @return boolean
475
+     */
476
+    public function isTokenPassword($password) {
477
+        try {
478
+            $this->tokenProvider->getToken($password);
479
+            return true;
480
+        } catch (InvalidTokenException $ex) {
481
+            return false;
482
+        }
483
+    }
484
+
485
+    protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) {
486
+        if ($refreshCsrfToken) {
487
+            // TODO: mock/inject/use non-static
488
+            // Refresh the token
489
+            \OC::$server->getCsrfTokenManager()->refreshToken();
490
+        }
491
+
492
+        //we need to pass the user name, which may differ from login name
493
+        $user = $this->getUser()->getUID();
494
+        OC_Util::setupFS($user);
495
+
496
+        if ($firstTimeLogin) {
497
+            // TODO: lock necessary?
498
+            //trigger creation of user home and /files folder
499
+            $userFolder = \OC::$server->getUserFolder($user);
500
+
501
+            try {
502
+                // copy skeleton
503
+                \OC_Util::copySkeleton($user, $userFolder);
504
+            } catch (NotPermittedException $ex) {
505
+                // read only uses
506
+            }
507
+
508
+            // trigger any other initialization
509
+            \OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
510
+        }
511
+    }
512
+
513
+    /**
514
+     * Tries to login the user with HTTP Basic Authentication
515
+     *
516
+     * @todo do not allow basic auth if the user is 2FA enforced
517
+     * @param IRequest $request
518
+     * @param OC\Security\Bruteforce\Throttler $throttler
519
+     * @return boolean if the login was successful
520
+     */
521
+    public function tryBasicAuthLogin(IRequest $request,
522
+                                        OC\Security\Bruteforce\Throttler $throttler) {
523
+        if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
524
+            try {
525
+                if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
526
+                    /**
527
+                     * Add DAV authenticated. This should in an ideal world not be
528
+                     * necessary but the iOS App reads cookies from anywhere instead
529
+                     * only the DAV endpoint.
530
+                     * This makes sure that the cookies will be valid for the whole scope
531
+                     * @see https://github.com/owncloud/core/issues/22893
532
+                     */
533
+                    $this->session->set(
534
+                        Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
535
+                    );
536
+
537
+                    // Set the last-password-confirm session to make the sudo mode work
538
+                        $this->session->set('last-password-confirm', $this->timeFactory->getTime());
539
+
540
+                    return true;
541
+                }
542
+            } catch (PasswordLoginForbiddenException $ex) {
543
+                // Nothing to do
544
+            }
545
+        }
546
+        return false;
547
+    }
548
+
549
+    /**
550
+     * Log an user in via login name and password
551
+     *
552
+     * @param string $uid
553
+     * @param string $password
554
+     * @return boolean
555
+     * @throws LoginException if an app canceld the login process or the user is not enabled
556
+     */
557
+    private function loginWithPassword($uid, $password) {
558
+        $user = $this->manager->checkPasswordNoLogging($uid, $password);
559
+        if ($user === false) {
560
+            // Password check failed
561
+            return false;
562
+        }
563
+
564
+        return $this->completeLogin($user, ['loginName' => $uid, 'password' => $password], false);
565
+    }
566
+
567
+    /**
568
+     * Log an user in with a given token (id)
569
+     *
570
+     * @param string $token
571
+     * @return boolean
572
+     * @throws LoginException if an app canceled the login process or the user is not enabled
573
+     */
574
+    private function loginWithToken($token) {
575
+        try {
576
+            $dbToken = $this->tokenProvider->getToken($token);
577
+        } catch (InvalidTokenException $ex) {
578
+            return false;
579
+        }
580
+        $uid = $dbToken->getUID();
581
+
582
+        // When logging in with token, the password must be decrypted first before passing to login hook
583
+        $password = '';
584
+        try {
585
+            $password = $this->tokenProvider->getPassword($dbToken, $token);
586
+        } catch (PasswordlessTokenException $ex) {
587
+            // Ignore and use empty string instead
588
+        }
589
+
590
+        $this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
591
+
592
+        $user = $this->manager->get($uid);
593
+        if (is_null($user)) {
594
+            // user does not exist
595
+            return false;
596
+        }
597
+
598
+        return $this->completeLogin(
599
+            $user,
600
+            [
601
+                'loginName' => $dbToken->getLoginName(),
602
+                'password' => $password,
603
+                'token' => $dbToken
604
+            ],
605
+            false);
606
+    }
607
+
608
+    /**
609
+     * Create a new session token for the given user credentials
610
+     *
611
+     * @param IRequest $request
612
+     * @param string $uid user UID
613
+     * @param string $loginName login name
614
+     * @param string $password
615
+     * @param int $remember
616
+     * @return boolean
617
+     */
618
+    public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
619
+        if (is_null($this->manager->get($uid))) {
620
+            // User does not exist
621
+            return false;
622
+        }
623
+        $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
624
+        try {
625
+            $sessionId = $this->session->getId();
626
+            $pwd = $this->getPassword($password);
627
+            // Make sure the current sessionId has no leftover tokens
628
+            $this->tokenProvider->invalidateToken($sessionId);
629
+            $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
630
+            return true;
631
+        } catch (SessionNotAvailableException $ex) {
632
+            // This can happen with OCC, where a memory session is used
633
+            // if a memory session is used, we shouldn't create a session token anyway
634
+            return false;
635
+        }
636
+    }
637
+
638
+    /**
639
+     * Checks if the given password is a token.
640
+     * If yes, the password is extracted from the token.
641
+     * If no, the same password is returned.
642
+     *
643
+     * @param string $password either the login password or a device token
644
+     * @return string|null the password or null if none was set in the token
645
+     */
646
+    private function getPassword($password) {
647
+        if (is_null($password)) {
648
+            // This is surely no token ;-)
649
+            return null;
650
+        }
651
+        try {
652
+            $token = $this->tokenProvider->getToken($password);
653
+            try {
654
+                return $this->tokenProvider->getPassword($token, $password);
655
+            } catch (PasswordlessTokenException $ex) {
656
+                return null;
657
+            }
658
+        } catch (InvalidTokenException $ex) {
659
+            return $password;
660
+        }
661
+    }
662
+
663
+    /**
664
+     * @param IToken $dbToken
665
+     * @param string $token
666
+     * @return boolean
667
+     */
668
+    private function checkTokenCredentials(IToken $dbToken, $token) {
669
+        // Check whether login credentials are still valid and the user was not disabled
670
+        // This check is performed each 5 minutes
671
+        $lastCheck = $dbToken->getLastCheck() ? : 0;
672
+        $now = $this->timeFactory->getTime();
673
+        if ($lastCheck > ($now - 60 * 5)) {
674
+            // Checked performed recently, nothing to do now
675
+            return true;
676
+        }
677
+
678
+        try {
679
+            $pwd = $this->tokenProvider->getPassword($dbToken, $token);
680
+        } catch (InvalidTokenException $ex) {
681
+            // An invalid token password was used -> log user out
682
+            return false;
683
+        } catch (PasswordlessTokenException $ex) {
684
+            // Token has no password
685
+
686
+            if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
687
+                $this->tokenProvider->invalidateToken($token);
688
+                return false;
689
+            }
690
+
691
+            $dbToken->setLastCheck($now);
692
+            return true;
693
+        }
694
+
695
+        if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
696
+            || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
697
+            $this->tokenProvider->invalidateToken($token);
698
+            // Password has changed or user was disabled -> log user out
699
+            return false;
700
+        }
701
+        $dbToken->setLastCheck($now);
702
+        return true;
703
+    }
704
+
705
+    /**
706
+     * Check if the given token exists and performs password/user-enabled checks
707
+     *
708
+     * Invalidates the token if checks fail
709
+     *
710
+     * @param string $token
711
+     * @param string $user login name
712
+     * @return boolean
713
+     */
714
+    private function validateToken($token, $user = null) {
715
+        try {
716
+            $dbToken = $this->tokenProvider->getToken($token);
717
+        } catch (InvalidTokenException $ex) {
718
+            return false;
719
+        }
720
+
721
+        // Check if login names match
722
+        if (!is_null($user) && $dbToken->getLoginName() !== $user) {
723
+            // TODO: this makes it imposssible to use different login names on browser and client
724
+            // e.g. login by e-mail '[email protected]' on browser for generating the token will not
725
+            //      allow to use the client token with the login name 'user'.
726
+            return false;
727
+        }
728
+
729
+        if (!$this->checkTokenCredentials($dbToken, $token)) {
730
+            return false;
731
+        }
732
+
733
+        $this->tokenProvider->updateTokenActivity($dbToken);
734
+
735
+        return true;
736
+    }
737
+
738
+    /**
739
+     * Tries to login the user with auth token header
740
+     *
741
+     * @param IRequest $request
742
+     * @todo check remember me cookie
743
+     * @return boolean
744
+     */
745
+    public function tryTokenLogin(IRequest $request) {
746
+        $authHeader = $request->getHeader('Authorization');
747
+        if (strpos($authHeader, 'Bearer ') === false) {
748
+            // No auth header, let's try session id
749
+            try {
750
+                $token = $this->session->getId();
751
+            } catch (SessionNotAvailableException $ex) {
752
+                return false;
753
+            }
754
+        } else {
755
+            $token = substr($authHeader, 7);
756
+        }
757
+
758
+        if (!$this->loginWithToken($token)) {
759
+            return false;
760
+        }
761
+        if(!$this->validateToken($token)) {
762
+            return false;
763
+        }
764
+        return true;
765
+    }
766
+
767
+    /**
768
+     * perform login using the magic cookie (remember login)
769
+     *
770
+     * @param string $uid the username
771
+     * @param string $currentToken
772
+     * @param string $oldSessionId
773
+     * @return bool
774
+     */
775
+    public function loginWithCookie($uid, $currentToken, $oldSessionId) {
776
+        $this->session->regenerateId();
777
+        $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
778
+        $user = $this->manager->get($uid);
779
+        if (is_null($user)) {
780
+            // user does not exist
781
+            return false;
782
+        }
783
+
784
+        // get stored tokens
785
+        $tokens = $this->config->getUserKeys($uid, 'login_token');
786
+        // test cookies token against stored tokens
787
+        if (!in_array($currentToken, $tokens, true)) {
788
+            return false;
789
+        }
790
+        // replace successfully used token with a new one
791
+        $this->config->deleteUserValue($uid, 'login_token', $currentToken);
792
+        $newToken = $this->random->generate(32);
793
+        $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime());
794
+
795
+        try {
796
+            $sessionId = $this->session->getId();
797
+            $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
798
+        } catch (SessionNotAvailableException $ex) {
799
+            return false;
800
+        } catch (InvalidTokenException $ex) {
801
+            \OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
802
+            return false;
803
+        }
804
+
805
+        $this->setMagicInCookie($user->getUID(), $newToken);
806
+        $token = $this->tokenProvider->getToken($sessionId);
807
+
808
+        //login
809
+        $this->setUser($user);
810
+        $this->setLoginName($token->getLoginName());
811
+        $this->setToken($token->getId());
812
+        $this->lockdownManager->setToken($token);
813
+        $user->updateLastLoginTimestamp();
814
+        $password = null;
815
+        try {
816
+            $password = $this->tokenProvider->getPassword($token, $sessionId);
817
+        } catch (PasswordlessTokenException $ex) {
818
+            // Ignore
819
+        }
820
+        $this->manager->emit('\OC\User', 'postRememberedLogin', [$user, $password]);
821
+        return true;
822
+    }
823
+
824
+    /**
825
+     * @param IUser $user
826
+     */
827
+    public function createRememberMeToken(IUser $user) {
828
+        $token = $this->random->generate(32);
829
+        $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime());
830
+        $this->setMagicInCookie($user->getUID(), $token);
831
+    }
832
+
833
+    /**
834
+     * logout the user from the session
835
+     */
836
+    public function logout() {
837
+        $this->manager->emit('\OC\User', 'logout');
838
+        $user = $this->getUser();
839
+        if (!is_null($user)) {
840
+            try {
841
+                $this->tokenProvider->invalidateToken($this->session->getId());
842
+            } catch (SessionNotAvailableException $ex) {
843
+
844
+            }
845
+        }
846
+        $this->setUser(null);
847
+        $this->setLoginName(null);
848
+        $this->setToken(null);
849
+        $this->unsetMagicInCookie();
850
+        $this->session->clear();
851
+        $this->manager->emit('\OC\User', 'postLogout');
852
+    }
853
+
854
+    /**
855
+     * Set cookie value to use in next page load
856
+     *
857
+     * @param string $username username to be set
858
+     * @param string $token
859
+     */
860
+    public function setMagicInCookie($username, $token) {
861
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
862
+        $webRoot = \OC::$WEBROOT;
863
+        if ($webRoot === '') {
864
+            $webRoot = '/';
865
+        }
866
+
867
+        $expires = $this->timeFactory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
868
+        setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
869
+        setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
870
+        try {
871
+            setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
872
+        } catch (SessionNotAvailableException $ex) {
873
+            // ignore
874
+        }
875
+    }
876
+
877
+    /**
878
+     * Remove cookie for "remember username"
879
+     */
880
+    public function unsetMagicInCookie() {
881
+        //TODO: DI for cookies and IRequest
882
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
883
+
884
+        unset($_COOKIE['nc_username']); //TODO: DI
885
+        unset($_COOKIE['nc_token']);
886
+        unset($_COOKIE['nc_session_id']);
887
+        setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
888
+        setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
889
+        setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
890
+        // old cookies might be stored under /webroot/ instead of /webroot
891
+        // and Firefox doesn't like it!
892
+        setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
893
+        setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
894
+        setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
895
+    }
896
+
897
+    /**
898
+     * Update password of the browser session token if there is one
899
+     *
900
+     * @param string $password
901
+     */
902
+    public function updateSessionTokenPassword($password) {
903
+        try {
904
+            $sessionId = $this->session->getId();
905
+            $token = $this->tokenProvider->getToken($sessionId);
906
+            $this->tokenProvider->setPassword($token, $sessionId, $password);
907
+        } catch (SessionNotAvailableException $ex) {
908
+            // Nothing to do
909
+        } catch (InvalidTokenException $ex) {
910
+            // Nothing to do
911
+        }
912
+    }
913 913
 
914 914
 
915 915
 }
Please login to merge, or discard this patch.
lib/private/Session/CryptoSessionData.php 1 patch
Indentation   +173 added lines, -173 removed lines patch added patch discarded remove patch
@@ -36,177 +36,177 @@
 block discarded – undo
36 36
  * @package OC\Session
37 37
  */
38 38
 class CryptoSessionData implements \ArrayAccess, ISession {
39
-	/** @var ISession */
40
-	protected $session;
41
-	/** @var \OCP\Security\ICrypto */
42
-	protected $crypto;
43
-	/** @var string */
44
-	protected $passphrase;
45
-	/** @var array */
46
-	protected $sessionValues;
47
-	/** @var bool */
48
-	protected $isModified = false;
49
-	CONST encryptedSessionName = 'encrypted_session_data';
50
-
51
-	/**
52
-	 * @param ISession $session
53
-	 * @param ICrypto $crypto
54
-	 * @param string $passphrase
55
-	 */
56
-	public function __construct(ISession $session,
57
-								ICrypto $crypto,
58
-								$passphrase) {
59
-		$this->crypto = $crypto;
60
-		$this->session = $session;
61
-		$this->passphrase = $passphrase;
62
-		$this->initializeSession();
63
-	}
64
-
65
-	/**
66
-	 * Close session if class gets destructed
67
-	 */
68
-	public function __destruct() {
69
-		try {
70
-			$this->close();
71
-		} catch (SessionNotAvailableException $e){
72
-			// This exception can occur if session is already closed
73
-			// So it is safe to ignore it and let the garbage collector to proceed
74
-		}
75
-	}
76
-
77
-	protected function initializeSession() {
78
-		$encryptedSessionData = $this->session->get(self::encryptedSessionName);
79
-		try {
80
-			$this->sessionValues = json_decode(
81
-				$this->crypto->decrypt($encryptedSessionData, $this->passphrase),
82
-				true
83
-			);
84
-		} catch (\Exception $e) {
85
-			$this->sessionValues = [];
86
-		}
87
-	}
88
-
89
-	/**
90
-	 * Set a value in the session
91
-	 *
92
-	 * @param string $key
93
-	 * @param mixed $value
94
-	 */
95
-	public function set($key, $value) {
96
-		$this->sessionValues[$key] = $value;
97
-		$this->isModified = true;
98
-	}
99
-
100
-	/**
101
-	 * Get a value from the session
102
-	 *
103
-	 * @param string $key
104
-	 * @return string|null Either the value or null
105
-	 */
106
-	public function get($key) {
107
-		if(isset($this->sessionValues[$key])) {
108
-			return $this->sessionValues[$key];
109
-		}
110
-
111
-		return null;
112
-	}
113
-
114
-	/**
115
-	 * Check if a named key exists in the session
116
-	 *
117
-	 * @param string $key
118
-	 * @return bool
119
-	 */
120
-	public function exists($key) {
121
-		return isset($this->sessionValues[$key]);
122
-	}
123
-
124
-	/**
125
-	 * Remove a $key/$value pair from the session
126
-	 *
127
-	 * @param string $key
128
-	 */
129
-	public function remove($key) {
130
-		$this->isModified = true;
131
-		unset($this->sessionValues[$key]);
132
-		$this->session->remove(self::encryptedSessionName);
133
-	}
134
-
135
-	/**
136
-	 * Reset and recreate the session
137
-	 */
138
-	public function clear() {
139
-		$requesttoken = $this->get('requesttoken');
140
-		$this->sessionValues = [];
141
-		if ($requesttoken !== null) {
142
-			$this->set('requesttoken', $requesttoken);
143
-		}
144
-		$this->isModified = true;
145
-		$this->session->clear();
146
-	}
147
-
148
-	/**
149
-	 * Wrapper around session_regenerate_id
150
-	 *
151
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
152
-	 * @param bool $updateToken Wheater to update the associated auth token
153
-	 * @return void
154
-	 */
155
-	public function regenerateId($deleteOldSession = true, $updateToken = false) {
156
-		$this->session->regenerateId($deleteOldSession, $updateToken);
157
-	}
158
-
159
-	/**
160
-	 * Wrapper around session_id
161
-	 *
162
-	 * @return string
163
-	 * @throws SessionNotAvailableException
164
-	 * @since 9.1.0
165
-	 */
166
-	public function getId() {
167
-		return $this->session->getId();
168
-	}
169
-
170
-	/**
171
-	 * Close the session and release the lock, also writes all changed data in batch
172
-	 */
173
-	public function close() {
174
-		if($this->isModified) {
175
-			$encryptedValue = $this->crypto->encrypt(json_encode($this->sessionValues), $this->passphrase);
176
-			$this->session->set(self::encryptedSessionName, $encryptedValue);
177
-			$this->isModified = false;
178
-		}
179
-		$this->session->close();
180
-	}
181
-
182
-	/**
183
-	 * @param mixed $offset
184
-	 * @return bool
185
-	 */
186
-	public function offsetExists($offset) {
187
-		return $this->exists($offset);
188
-	}
189
-
190
-	/**
191
-	 * @param mixed $offset
192
-	 * @return mixed
193
-	 */
194
-	public function offsetGet($offset) {
195
-		return $this->get($offset);
196
-	}
197
-
198
-	/**
199
-	 * @param mixed $offset
200
-	 * @param mixed $value
201
-	 */
202
-	public function offsetSet($offset, $value) {
203
-		$this->set($offset, $value);
204
-	}
205
-
206
-	/**
207
-	 * @param mixed $offset
208
-	 */
209
-	public function offsetUnset($offset) {
210
-		$this->remove($offset);
211
-	}
39
+    /** @var ISession */
40
+    protected $session;
41
+    /** @var \OCP\Security\ICrypto */
42
+    protected $crypto;
43
+    /** @var string */
44
+    protected $passphrase;
45
+    /** @var array */
46
+    protected $sessionValues;
47
+    /** @var bool */
48
+    protected $isModified = false;
49
+    CONST encryptedSessionName = 'encrypted_session_data';
50
+
51
+    /**
52
+     * @param ISession $session
53
+     * @param ICrypto $crypto
54
+     * @param string $passphrase
55
+     */
56
+    public function __construct(ISession $session,
57
+                                ICrypto $crypto,
58
+                                $passphrase) {
59
+        $this->crypto = $crypto;
60
+        $this->session = $session;
61
+        $this->passphrase = $passphrase;
62
+        $this->initializeSession();
63
+    }
64
+
65
+    /**
66
+     * Close session if class gets destructed
67
+     */
68
+    public function __destruct() {
69
+        try {
70
+            $this->close();
71
+        } catch (SessionNotAvailableException $e){
72
+            // This exception can occur if session is already closed
73
+            // So it is safe to ignore it and let the garbage collector to proceed
74
+        }
75
+    }
76
+
77
+    protected function initializeSession() {
78
+        $encryptedSessionData = $this->session->get(self::encryptedSessionName);
79
+        try {
80
+            $this->sessionValues = json_decode(
81
+                $this->crypto->decrypt($encryptedSessionData, $this->passphrase),
82
+                true
83
+            );
84
+        } catch (\Exception $e) {
85
+            $this->sessionValues = [];
86
+        }
87
+    }
88
+
89
+    /**
90
+     * Set a value in the session
91
+     *
92
+     * @param string $key
93
+     * @param mixed $value
94
+     */
95
+    public function set($key, $value) {
96
+        $this->sessionValues[$key] = $value;
97
+        $this->isModified = true;
98
+    }
99
+
100
+    /**
101
+     * Get a value from the session
102
+     *
103
+     * @param string $key
104
+     * @return string|null Either the value or null
105
+     */
106
+    public function get($key) {
107
+        if(isset($this->sessionValues[$key])) {
108
+            return $this->sessionValues[$key];
109
+        }
110
+
111
+        return null;
112
+    }
113
+
114
+    /**
115
+     * Check if a named key exists in the session
116
+     *
117
+     * @param string $key
118
+     * @return bool
119
+     */
120
+    public function exists($key) {
121
+        return isset($this->sessionValues[$key]);
122
+    }
123
+
124
+    /**
125
+     * Remove a $key/$value pair from the session
126
+     *
127
+     * @param string $key
128
+     */
129
+    public function remove($key) {
130
+        $this->isModified = true;
131
+        unset($this->sessionValues[$key]);
132
+        $this->session->remove(self::encryptedSessionName);
133
+    }
134
+
135
+    /**
136
+     * Reset and recreate the session
137
+     */
138
+    public function clear() {
139
+        $requesttoken = $this->get('requesttoken');
140
+        $this->sessionValues = [];
141
+        if ($requesttoken !== null) {
142
+            $this->set('requesttoken', $requesttoken);
143
+        }
144
+        $this->isModified = true;
145
+        $this->session->clear();
146
+    }
147
+
148
+    /**
149
+     * Wrapper around session_regenerate_id
150
+     *
151
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
152
+     * @param bool $updateToken Wheater to update the associated auth token
153
+     * @return void
154
+     */
155
+    public function regenerateId($deleteOldSession = true, $updateToken = false) {
156
+        $this->session->regenerateId($deleteOldSession, $updateToken);
157
+    }
158
+
159
+    /**
160
+     * Wrapper around session_id
161
+     *
162
+     * @return string
163
+     * @throws SessionNotAvailableException
164
+     * @since 9.1.0
165
+     */
166
+    public function getId() {
167
+        return $this->session->getId();
168
+    }
169
+
170
+    /**
171
+     * Close the session and release the lock, also writes all changed data in batch
172
+     */
173
+    public function close() {
174
+        if($this->isModified) {
175
+            $encryptedValue = $this->crypto->encrypt(json_encode($this->sessionValues), $this->passphrase);
176
+            $this->session->set(self::encryptedSessionName, $encryptedValue);
177
+            $this->isModified = false;
178
+        }
179
+        $this->session->close();
180
+    }
181
+
182
+    /**
183
+     * @param mixed $offset
184
+     * @return bool
185
+     */
186
+    public function offsetExists($offset) {
187
+        return $this->exists($offset);
188
+    }
189
+
190
+    /**
191
+     * @param mixed $offset
192
+     * @return mixed
193
+     */
194
+    public function offsetGet($offset) {
195
+        return $this->get($offset);
196
+    }
197
+
198
+    /**
199
+     * @param mixed $offset
200
+     * @param mixed $value
201
+     */
202
+    public function offsetSet($offset, $value) {
203
+        $this->set($offset, $value);
204
+    }
205
+
206
+    /**
207
+     * @param mixed $offset
208
+     */
209
+    public function offsetUnset($offset) {
210
+        $this->remove($offset);
211
+    }
212 212
 }
Please login to merge, or discard this patch.
lib/private/Session/Memory.php 1 patch
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -38,86 +38,86 @@
 block discarded – undo
38 38
  * @package OC\Session
39 39
  */
40 40
 class Memory extends Session {
41
-	protected $data;
41
+    protected $data;
42 42
 
43
-	public function __construct($name) {
44
-		//no need to use $name since all data is already scoped to this instance
45
-		$this->data = array();
46
-	}
43
+    public function __construct($name) {
44
+        //no need to use $name since all data is already scoped to this instance
45
+        $this->data = array();
46
+    }
47 47
 
48
-	/**
49
-	 * @param string $key
50
-	 * @param integer $value
51
-	 */
52
-	public function set($key, $value) {
53
-		$this->validateSession();
54
-		$this->data[$key] = $value;
55
-	}
48
+    /**
49
+     * @param string $key
50
+     * @param integer $value
51
+     */
52
+    public function set($key, $value) {
53
+        $this->validateSession();
54
+        $this->data[$key] = $value;
55
+    }
56 56
 
57
-	/**
58
-	 * @param string $key
59
-	 * @return mixed
60
-	 */
61
-	public function get($key) {
62
-		if (!$this->exists($key)) {
63
-			return null;
64
-		}
65
-		return $this->data[$key];
66
-	}
57
+    /**
58
+     * @param string $key
59
+     * @return mixed
60
+     */
61
+    public function get($key) {
62
+        if (!$this->exists($key)) {
63
+            return null;
64
+        }
65
+        return $this->data[$key];
66
+    }
67 67
 
68
-	/**
69
-	 * @param string $key
70
-	 * @return bool
71
-	 */
72
-	public function exists($key) {
73
-		return isset($this->data[$key]);
74
-	}
68
+    /**
69
+     * @param string $key
70
+     * @return bool
71
+     */
72
+    public function exists($key) {
73
+        return isset($this->data[$key]);
74
+    }
75 75
 
76
-	/**
77
-	 * @param string $key
78
-	 */
79
-	public function remove($key) {
80
-		$this->validateSession();
81
-		unset($this->data[$key]);
82
-	}
76
+    /**
77
+     * @param string $key
78
+     */
79
+    public function remove($key) {
80
+        $this->validateSession();
81
+        unset($this->data[$key]);
82
+    }
83 83
 
84
-	public function clear() {
85
-		$this->data = array();
86
-	}
84
+    public function clear() {
85
+        $this->data = array();
86
+    }
87 87
 
88
-	/**
89
-	 * Stub since the session ID does not need to get regenerated for the cache
90
-	 *
91
-	 * @param bool $deleteOldSession
92
-	 */
93
-	public function regenerateId($deleteOldSession = true, $updateToken = false) {}
88
+    /**
89
+     * Stub since the session ID does not need to get regenerated for the cache
90
+     *
91
+     * @param bool $deleteOldSession
92
+     */
93
+    public function regenerateId($deleteOldSession = true, $updateToken = false) {}
94 94
 
95
-	/**
96
-	 * Wrapper around session_id
97
-	 *
98
-	 * @return string
99
-	 * @throws SessionNotAvailableException
100
-	 * @since 9.1.0
101
-	 */
102
-	public function getId() {
103
-		throw new SessionNotAvailableException('Memory session does not have an ID');
104
-	}
95
+    /**
96
+     * Wrapper around session_id
97
+     *
98
+     * @return string
99
+     * @throws SessionNotAvailableException
100
+     * @since 9.1.0
101
+     */
102
+    public function getId() {
103
+        throw new SessionNotAvailableException('Memory session does not have an ID');
104
+    }
105 105
 
106
-	/**
107
-	 * Helper function for PHPUnit execution - don't use in non-test code
108
-	 */
109
-	public function reopen() {
110
-		$this->sessionClosed = false;
111
-	}
106
+    /**
107
+     * Helper function for PHPUnit execution - don't use in non-test code
108
+     */
109
+    public function reopen() {
110
+        $this->sessionClosed = false;
111
+    }
112 112
 
113
-	/**
114
-	 * In case the session has already been locked an exception will be thrown
115
-	 *
116
-	 * @throws Exception
117
-	 */
118
-	private function validateSession() {
119
-		if ($this->sessionClosed) {
120
-			throw new Exception('Session has been closed - no further changes to the session are allowed');
121
-		}
122
-	}
113
+    /**
114
+     * In case the session has already been locked an exception will be thrown
115
+     *
116
+     * @throws Exception
117
+     */
118
+    private function validateSession() {
119
+        if ($this->sessionClosed) {
120
+            throw new Exception('Session has been closed - no further changes to the session are allowed');
121
+        }
122
+    }
123 123
 }
Please login to merge, or discard this patch.
lib/public/ISession.php 1 patch
Indentation   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -42,69 +42,69 @@
 block discarded – undo
42 42
  */
43 43
 interface ISession {
44 44
 
45
-	/**
46
-	 * Set a value in the session
47
-	 *
48
-	 * @param string $key
49
-	 * @param mixed $value
50
-	 * @since 6.0.0
51
-	 */
52
-	public function set($key, $value);
45
+    /**
46
+     * Set a value in the session
47
+     *
48
+     * @param string $key
49
+     * @param mixed $value
50
+     * @since 6.0.0
51
+     */
52
+    public function set($key, $value);
53 53
 
54
-	/**
55
-	 * Get a value from the session
56
-	 *
57
-	 * @param string $key
58
-	 * @return mixed should return null if $key does not exist
59
-	 * @since 6.0.0
60
-	 */
61
-	public function get($key);
54
+    /**
55
+     * Get a value from the session
56
+     *
57
+     * @param string $key
58
+     * @return mixed should return null if $key does not exist
59
+     * @since 6.0.0
60
+     */
61
+    public function get($key);
62 62
 
63
-	/**
64
-	 * Check if a named key exists in the session
65
-	 *
66
-	 * @param string $key
67
-	 * @return bool
68
-	 * @since 6.0.0
69
-	 */
70
-	public function exists($key);
63
+    /**
64
+     * Check if a named key exists in the session
65
+     *
66
+     * @param string $key
67
+     * @return bool
68
+     * @since 6.0.0
69
+     */
70
+    public function exists($key);
71 71
 
72
-	/**
73
-	 * Remove a $key/$value pair from the session
74
-	 *
75
-	 * @param string $key
76
-	 * @since 6.0.0
77
-	 */
78
-	public function remove($key);
72
+    /**
73
+     * Remove a $key/$value pair from the session
74
+     *
75
+     * @param string $key
76
+     * @since 6.0.0
77
+     */
78
+    public function remove($key);
79 79
 
80
-	/**
81
-	 * Reset and recreate the session
82
-	 * @since 6.0.0
83
-	 */
84
-	public function clear();
80
+    /**
81
+     * Reset and recreate the session
82
+     * @since 6.0.0
83
+     */
84
+    public function clear();
85 85
 
86
-	/**
87
-	 * Close the session and release the lock
88
-	 * @since 7.0.0
89
-	 */
90
-	public function close();
86
+    /**
87
+     * Close the session and release the lock
88
+     * @since 7.0.0
89
+     */
90
+    public function close();
91 91
 
92
-	/**
93
-	 * Wrapper around session_regenerate_id
94
-	 *
95
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
96
-	 * @param bool $updateToken Wheater to update the associated auth token
97
-	 * @return void
98
-	 * @since 9.0.0, $updateToken added in 14.0.0
99
-	 */
100
-	public function regenerateId($deleteOldSession = true, $updateToken = false);
92
+    /**
93
+     * Wrapper around session_regenerate_id
94
+     *
95
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
96
+     * @param bool $updateToken Wheater to update the associated auth token
97
+     * @return void
98
+     * @since 9.0.0, $updateToken added in 14.0.0
99
+     */
100
+    public function regenerateId($deleteOldSession = true, $updateToken = false);
101 101
 
102
-	/**
103
-	 * Wrapper around session_id
104
-	 *
105
-	 * @return string
106
-	 * @throws SessionNotAvailableException
107
-	 * @since 9.1.0
108
-	 */
109
-	public function getId();
102
+    /**
103
+     * Wrapper around session_id
104
+     *
105
+     * @return string
106
+     * @throws SessionNotAvailableException
107
+     * @since 9.1.0
108
+     */
109
+    public function getId();
110 110
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 2 patches
Indentation   +576 added lines, -576 removed lines patch added patch discarded remove patch
@@ -67,584 +67,584 @@
 block discarded – undo
67 67
  */
68 68
 class ShareController extends Controller {
69 69
 
70
-	/** @var IConfig */
71
-	protected $config;
72
-	/** @var IURLGenerator */
73
-	protected $urlGenerator;
74
-	/** @var IUserManager */
75
-	protected $userManager;
76
-	/** @var ILogger */
77
-	protected $logger;
78
-	/** @var \OCP\Activity\IManager */
79
-	protected $activityManager;
80
-	/** @var \OCP\Share\IManager */
81
-	protected $shareManager;
82
-	/** @var ISession */
83
-	protected $session;
84
-	/** @var IPreview */
85
-	protected $previewManager;
86
-	/** @var IRootFolder */
87
-	protected $rootFolder;
88
-	/** @var FederatedShareProvider */
89
-	protected $federatedShareProvider;
90
-	/** @var EventDispatcherInterface */
91
-	protected $eventDispatcher;
92
-	/** @var IL10N */
93
-	protected $l10n;
94
-	/** @var Defaults */
95
-	protected $defaults;
96
-
97
-	/**
98
-	 * @param string $appName
99
-	 * @param IRequest $request
100
-	 * @param IConfig $config
101
-	 * @param IURLGenerator $urlGenerator
102
-	 * @param IUserManager $userManager
103
-	 * @param ILogger $logger
104
-	 * @param \OCP\Activity\IManager $activityManager
105
-	 * @param \OCP\Share\IManager $shareManager
106
-	 * @param ISession $session
107
-	 * @param IPreview $previewManager
108
-	 * @param IRootFolder $rootFolder
109
-	 * @param FederatedShareProvider $federatedShareProvider
110
-	 * @param EventDispatcherInterface $eventDispatcher
111
-	 * @param IL10N $l10n
112
-	 * @param Defaults $defaults
113
-	 */
114
-	public function __construct($appName,
115
-								IRequest $request,
116
-								IConfig $config,
117
-								IURLGenerator $urlGenerator,
118
-								IUserManager $userManager,
119
-								ILogger $logger,
120
-								\OCP\Activity\IManager $activityManager,
121
-								\OCP\Share\IManager $shareManager,
122
-								ISession $session,
123
-								IPreview $previewManager,
124
-								IRootFolder $rootFolder,
125
-								FederatedShareProvider $federatedShareProvider,
126
-								EventDispatcherInterface $eventDispatcher,
127
-								IL10N $l10n,
128
-								Defaults $defaults) {
129
-		parent::__construct($appName, $request);
130
-
131
-		$this->config = $config;
132
-		$this->urlGenerator = $urlGenerator;
133
-		$this->userManager = $userManager;
134
-		$this->logger = $logger;
135
-		$this->activityManager = $activityManager;
136
-		$this->shareManager = $shareManager;
137
-		$this->session = $session;
138
-		$this->previewManager = $previewManager;
139
-		$this->rootFolder = $rootFolder;
140
-		$this->federatedShareProvider = $federatedShareProvider;
141
-		$this->eventDispatcher = $eventDispatcher;
142
-		$this->l10n = $l10n;
143
-		$this->defaults = $defaults;
144
-	}
145
-
146
-	/**
147
-	 * @PublicPage
148
-	 * @NoCSRFRequired
149
-	 *
150
-	 * @param string $token
151
-	 * @return TemplateResponse|RedirectResponse
152
-	 */
153
-	public function showAuthenticate($token) {
154
-		$share = $this->shareManager->getShareByToken($token);
155
-
156
-		if($this->linkShareAuth($share)) {
157
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
158
-		}
159
-
160
-		return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
161
-	}
162
-
163
-	/**
164
-	 * @PublicPage
165
-	 * @UseSession
166
-	 * @BruteForceProtection(action=publicLinkAuth)
167
-	 *
168
-	 * Authenticates against password-protected shares
169
-	 * @param string $token
170
-	 * @param string $password
171
-	 * @return RedirectResponse|TemplateResponse|NotFoundResponse
172
-	 */
173
-	public function authenticate($token, $password = '') {
174
-
175
-		// Check whether share exists
176
-		try {
177
-			$share = $this->shareManager->getShareByToken($token);
178
-		} catch (ShareNotFound $e) {
179
-			return new NotFoundResponse();
180
-		}
181
-
182
-		$authenticate = $this->linkShareAuth($share, $password);
183
-
184
-		if($authenticate === true) {
185
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
186
-		}
187
-
188
-		$response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
189
-		$response->throttle();
190
-		return $response;
191
-	}
192
-
193
-	/**
194
-	 * Authenticate a link item with the given password.
195
-	 * Or use the session if no password is provided.
196
-	 *
197
-	 * This is a modified version of Helper::authenticate
198
-	 * TODO: Try to merge back eventually with Helper::authenticate
199
-	 *
200
-	 * @param \OCP\Share\IShare $share
201
-	 * @param string|null $password
202
-	 * @return bool
203
-	 */
204
-	private function linkShareAuth(\OCP\Share\IShare $share, $password = null) {
205
-		if ($password !== null) {
206
-			if ($this->shareManager->checkPassword($share, $password)) {
207
-				$this->session->regenerateId(true, true);
208
-				$this->session->set('public_link_authenticated', (string)$share->getId());
209
-			} else {
210
-				$this->emitAccessShareHook($share, 403, 'Wrong password');
211
-				return false;
212
-			}
213
-		} else {
214
-			// not authenticated ?
215
-			if ( ! $this->session->exists('public_link_authenticated')
216
-				|| $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
217
-				return false;
218
-			}
219
-		}
220
-		return true;
221
-	}
222
-
223
-	/**
224
-	 * throws hooks when a share is attempted to be accessed
225
-	 *
226
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
227
-	 * otherwise token
228
-	 * @param int $errorCode
229
-	 * @param string $errorMessage
230
-	 * @throws \OC\HintException
231
-	 * @throws \OC\ServerNotAvailableException
232
-	 */
233
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
234
-		$itemType = $itemSource = $uidOwner = '';
235
-		$token = $share;
236
-		$exception = null;
237
-		if($share instanceof \OCP\Share\IShare) {
238
-			try {
239
-				$token = $share->getToken();
240
-				$uidOwner = $share->getSharedBy();
241
-				$itemType = $share->getNodeType();
242
-				$itemSource = $share->getNodeId();
243
-			} catch (\Exception $e) {
244
-				// we log what we know and pass on the exception afterwards
245
-				$exception = $e;
246
-			}
247
-		}
248
-		\OC_Hook::emit('OCP\Share', 'share_link_access', [
249
-			'itemType' => $itemType,
250
-			'itemSource' => $itemSource,
251
-			'uidOwner' => $uidOwner,
252
-			'token' => $token,
253
-			'errorCode' => $errorCode,
254
-			'errorMessage' => $errorMessage,
255
-		]);
256
-		if(!is_null($exception)) {
257
-			throw $exception;
258
-		}
259
-	}
260
-
261
-	/**
262
-	 * Validate the permissions of the share
263
-	 *
264
-	 * @param Share\IShare $share
265
-	 * @return bool
266
-	 */
267
-	private function validateShare(\OCP\Share\IShare $share) {
268
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
269
-	}
270
-
271
-	/**
272
-	 * @PublicPage
273
-	 * @NoCSRFRequired
274
-	 *
275
-	 * @param string $token
276
-	 * @param string $path
277
-	 * @return TemplateResponse|RedirectResponse|NotFoundResponse
278
-	 * @throws NotFoundException
279
-	 * @throws \Exception
280
-	 */
281
-	public function showShare($token, $path = '') {
282
-		\OC_User::setIncognitoMode(true);
283
-
284
-		// Check whether share exists
285
-		try {
286
-			$share = $this->shareManager->getShareByToken($token);
287
-		} catch (ShareNotFound $e) {
288
-			$this->emitAccessShareHook($token, 404, 'Share not found');
289
-			return new NotFoundResponse();
290
-		}
291
-
292
-		// Share is password protected - check whether the user is permitted to access the share
293
-		if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
294
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
295
-				array('token' => $token)));
296
-		}
297
-
298
-		if (!$this->validateShare($share)) {
299
-			throw new NotFoundException();
300
-		}
301
-		// We can't get the path of a file share
302
-		try {
303
-			if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
304
-				$this->emitAccessShareHook($share, 404, 'Share not found');
305
-				throw new NotFoundException();
306
-			}
307
-		} catch (\Exception $e) {
308
-			$this->emitAccessShareHook($share, 404, 'Share not found');
309
-			throw $e;
310
-		}
311
-
312
-		$shareTmpl = [];
313
-		$shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
314
-		$shareTmpl['owner'] = $share->getShareOwner();
315
-		$shareTmpl['filename'] = $share->getNode()->getName();
316
-		$shareTmpl['directory_path'] = $share->getTarget();
317
-		$shareTmpl['mimetype'] = $share->getNode()->getMimetype();
318
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
319
-		$shareTmpl['dirToken'] = $token;
320
-		$shareTmpl['sharingToken'] = $token;
321
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
322
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
323
-		$shareTmpl['dir'] = '';
324
-		$shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
325
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
326
-
327
-		// Show file list
328
-		$hideFileList = false;
329
-		if ($share->getNode() instanceof \OCP\Files\Folder) {
330
-			/** @var \OCP\Files\Folder $rootFolder */
331
-			$rootFolder = $share->getNode();
332
-
333
-			try {
334
-				$folderNode = $rootFolder->get($path);
335
-			} catch (\OCP\Files\NotFoundException $e) {
336
-				$this->emitAccessShareHook($share, 404, 'Share not found');
337
-				throw new NotFoundException();
338
-			}
339
-
340
-			$shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
341
-
342
-			/*
70
+    /** @var IConfig */
71
+    protected $config;
72
+    /** @var IURLGenerator */
73
+    protected $urlGenerator;
74
+    /** @var IUserManager */
75
+    protected $userManager;
76
+    /** @var ILogger */
77
+    protected $logger;
78
+    /** @var \OCP\Activity\IManager */
79
+    protected $activityManager;
80
+    /** @var \OCP\Share\IManager */
81
+    protected $shareManager;
82
+    /** @var ISession */
83
+    protected $session;
84
+    /** @var IPreview */
85
+    protected $previewManager;
86
+    /** @var IRootFolder */
87
+    protected $rootFolder;
88
+    /** @var FederatedShareProvider */
89
+    protected $federatedShareProvider;
90
+    /** @var EventDispatcherInterface */
91
+    protected $eventDispatcher;
92
+    /** @var IL10N */
93
+    protected $l10n;
94
+    /** @var Defaults */
95
+    protected $defaults;
96
+
97
+    /**
98
+     * @param string $appName
99
+     * @param IRequest $request
100
+     * @param IConfig $config
101
+     * @param IURLGenerator $urlGenerator
102
+     * @param IUserManager $userManager
103
+     * @param ILogger $logger
104
+     * @param \OCP\Activity\IManager $activityManager
105
+     * @param \OCP\Share\IManager $shareManager
106
+     * @param ISession $session
107
+     * @param IPreview $previewManager
108
+     * @param IRootFolder $rootFolder
109
+     * @param FederatedShareProvider $federatedShareProvider
110
+     * @param EventDispatcherInterface $eventDispatcher
111
+     * @param IL10N $l10n
112
+     * @param Defaults $defaults
113
+     */
114
+    public function __construct($appName,
115
+                                IRequest $request,
116
+                                IConfig $config,
117
+                                IURLGenerator $urlGenerator,
118
+                                IUserManager $userManager,
119
+                                ILogger $logger,
120
+                                \OCP\Activity\IManager $activityManager,
121
+                                \OCP\Share\IManager $shareManager,
122
+                                ISession $session,
123
+                                IPreview $previewManager,
124
+                                IRootFolder $rootFolder,
125
+                                FederatedShareProvider $federatedShareProvider,
126
+                                EventDispatcherInterface $eventDispatcher,
127
+                                IL10N $l10n,
128
+                                Defaults $defaults) {
129
+        parent::__construct($appName, $request);
130
+
131
+        $this->config = $config;
132
+        $this->urlGenerator = $urlGenerator;
133
+        $this->userManager = $userManager;
134
+        $this->logger = $logger;
135
+        $this->activityManager = $activityManager;
136
+        $this->shareManager = $shareManager;
137
+        $this->session = $session;
138
+        $this->previewManager = $previewManager;
139
+        $this->rootFolder = $rootFolder;
140
+        $this->federatedShareProvider = $federatedShareProvider;
141
+        $this->eventDispatcher = $eventDispatcher;
142
+        $this->l10n = $l10n;
143
+        $this->defaults = $defaults;
144
+    }
145
+
146
+    /**
147
+     * @PublicPage
148
+     * @NoCSRFRequired
149
+     *
150
+     * @param string $token
151
+     * @return TemplateResponse|RedirectResponse
152
+     */
153
+    public function showAuthenticate($token) {
154
+        $share = $this->shareManager->getShareByToken($token);
155
+
156
+        if($this->linkShareAuth($share)) {
157
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
158
+        }
159
+
160
+        return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
161
+    }
162
+
163
+    /**
164
+     * @PublicPage
165
+     * @UseSession
166
+     * @BruteForceProtection(action=publicLinkAuth)
167
+     *
168
+     * Authenticates against password-protected shares
169
+     * @param string $token
170
+     * @param string $password
171
+     * @return RedirectResponse|TemplateResponse|NotFoundResponse
172
+     */
173
+    public function authenticate($token, $password = '') {
174
+
175
+        // Check whether share exists
176
+        try {
177
+            $share = $this->shareManager->getShareByToken($token);
178
+        } catch (ShareNotFound $e) {
179
+            return new NotFoundResponse();
180
+        }
181
+
182
+        $authenticate = $this->linkShareAuth($share, $password);
183
+
184
+        if($authenticate === true) {
185
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
186
+        }
187
+
188
+        $response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
189
+        $response->throttle();
190
+        return $response;
191
+    }
192
+
193
+    /**
194
+     * Authenticate a link item with the given password.
195
+     * Or use the session if no password is provided.
196
+     *
197
+     * This is a modified version of Helper::authenticate
198
+     * TODO: Try to merge back eventually with Helper::authenticate
199
+     *
200
+     * @param \OCP\Share\IShare $share
201
+     * @param string|null $password
202
+     * @return bool
203
+     */
204
+    private function linkShareAuth(\OCP\Share\IShare $share, $password = null) {
205
+        if ($password !== null) {
206
+            if ($this->shareManager->checkPassword($share, $password)) {
207
+                $this->session->regenerateId(true, true);
208
+                $this->session->set('public_link_authenticated', (string)$share->getId());
209
+            } else {
210
+                $this->emitAccessShareHook($share, 403, 'Wrong password');
211
+                return false;
212
+            }
213
+        } else {
214
+            // not authenticated ?
215
+            if ( ! $this->session->exists('public_link_authenticated')
216
+                || $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
217
+                return false;
218
+            }
219
+        }
220
+        return true;
221
+    }
222
+
223
+    /**
224
+     * throws hooks when a share is attempted to be accessed
225
+     *
226
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
227
+     * otherwise token
228
+     * @param int $errorCode
229
+     * @param string $errorMessage
230
+     * @throws \OC\HintException
231
+     * @throws \OC\ServerNotAvailableException
232
+     */
233
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
234
+        $itemType = $itemSource = $uidOwner = '';
235
+        $token = $share;
236
+        $exception = null;
237
+        if($share instanceof \OCP\Share\IShare) {
238
+            try {
239
+                $token = $share->getToken();
240
+                $uidOwner = $share->getSharedBy();
241
+                $itemType = $share->getNodeType();
242
+                $itemSource = $share->getNodeId();
243
+            } catch (\Exception $e) {
244
+                // we log what we know and pass on the exception afterwards
245
+                $exception = $e;
246
+            }
247
+        }
248
+        \OC_Hook::emit('OCP\Share', 'share_link_access', [
249
+            'itemType' => $itemType,
250
+            'itemSource' => $itemSource,
251
+            'uidOwner' => $uidOwner,
252
+            'token' => $token,
253
+            'errorCode' => $errorCode,
254
+            'errorMessage' => $errorMessage,
255
+        ]);
256
+        if(!is_null($exception)) {
257
+            throw $exception;
258
+        }
259
+    }
260
+
261
+    /**
262
+     * Validate the permissions of the share
263
+     *
264
+     * @param Share\IShare $share
265
+     * @return bool
266
+     */
267
+    private function validateShare(\OCP\Share\IShare $share) {
268
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
269
+    }
270
+
271
+    /**
272
+     * @PublicPage
273
+     * @NoCSRFRequired
274
+     *
275
+     * @param string $token
276
+     * @param string $path
277
+     * @return TemplateResponse|RedirectResponse|NotFoundResponse
278
+     * @throws NotFoundException
279
+     * @throws \Exception
280
+     */
281
+    public function showShare($token, $path = '') {
282
+        \OC_User::setIncognitoMode(true);
283
+
284
+        // Check whether share exists
285
+        try {
286
+            $share = $this->shareManager->getShareByToken($token);
287
+        } catch (ShareNotFound $e) {
288
+            $this->emitAccessShareHook($token, 404, 'Share not found');
289
+            return new NotFoundResponse();
290
+        }
291
+
292
+        // Share is password protected - check whether the user is permitted to access the share
293
+        if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
294
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
295
+                array('token' => $token)));
296
+        }
297
+
298
+        if (!$this->validateShare($share)) {
299
+            throw new NotFoundException();
300
+        }
301
+        // We can't get the path of a file share
302
+        try {
303
+            if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
304
+                $this->emitAccessShareHook($share, 404, 'Share not found');
305
+                throw new NotFoundException();
306
+            }
307
+        } catch (\Exception $e) {
308
+            $this->emitAccessShareHook($share, 404, 'Share not found');
309
+            throw $e;
310
+        }
311
+
312
+        $shareTmpl = [];
313
+        $shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
314
+        $shareTmpl['owner'] = $share->getShareOwner();
315
+        $shareTmpl['filename'] = $share->getNode()->getName();
316
+        $shareTmpl['directory_path'] = $share->getTarget();
317
+        $shareTmpl['mimetype'] = $share->getNode()->getMimetype();
318
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
319
+        $shareTmpl['dirToken'] = $token;
320
+        $shareTmpl['sharingToken'] = $token;
321
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
322
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
323
+        $shareTmpl['dir'] = '';
324
+        $shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
325
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
326
+
327
+        // Show file list
328
+        $hideFileList = false;
329
+        if ($share->getNode() instanceof \OCP\Files\Folder) {
330
+            /** @var \OCP\Files\Folder $rootFolder */
331
+            $rootFolder = $share->getNode();
332
+
333
+            try {
334
+                $folderNode = $rootFolder->get($path);
335
+            } catch (\OCP\Files\NotFoundException $e) {
336
+                $this->emitAccessShareHook($share, 404, 'Share not found');
337
+                throw new NotFoundException();
338
+            }
339
+
340
+            $shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
341
+
342
+            /*
343 343
 			 * The OC_Util methods require a view. This just uses the node API
344 344
 			 */
345
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
346
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
347
-				$freeSpace = max($freeSpace, 0);
348
-			} else {
349
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
350
-			}
351
-
352
-			$hideFileList = $share->getPermissions() & \OCP\Constants::PERMISSION_READ ? false : true;
353
-			$maxUploadFilesize = $freeSpace;
354
-
355
-			$folder = new Template('files', 'list', '');
356
-			$folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
357
-			$folder->assign('dirToken', $token);
358
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
359
-			$folder->assign('isPublic', true);
360
-			$folder->assign('hideFileList', $hideFileList);
361
-			$folder->assign('publicUploadEnabled', 'no');
362
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
363
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
364
-			$folder->assign('freeSpace', $freeSpace);
365
-			$folder->assign('usedSpacePercent', 0);
366
-			$folder->assign('trash', false);
367
-			$shareTmpl['folder'] = $folder->fetchPage();
368
-		}
369
-
370
-		$shareTmpl['hideFileList'] = $hideFileList;
371
-		$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
372
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $token]);
373
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
374
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
375
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
376
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
377
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
378
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
379
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
380
-		$ogPreview = '';
381
-		if ($shareTmpl['previewSupported']) {
382
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
383
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
384
-			$ogPreview = $shareTmpl['previewImage'];
385
-
386
-			// We just have direct previews for image files
387
-			if ($share->getNode()->getMimePart() === 'image') {
388
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
389
-
390
-				$ogPreview = $shareTmpl['previewURL'];
391
-
392
-				//Whatapp is kind of picky about their size requirements
393
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
394
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
395
-						't' => $token,
396
-						'x' => 256,
397
-						'y' => 256,
398
-						'a' => true,
399
-					]);
400
-				}
401
-			}
402
-		} else {
403
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
404
-			$ogPreview = $shareTmpl['previewImage'];
405
-		}
406
-
407
-		// Load files we need
408
-		\OCP\Util::addScript('files', 'file-upload');
409
-		\OCP\Util::addStyle('files_sharing', 'publicView');
410
-		\OCP\Util::addScript('files_sharing', 'public');
411
-		\OCP\Util::addScript('files', 'fileactions');
412
-		\OCP\Util::addScript('files', 'fileactionsmenu');
413
-		\OCP\Util::addScript('files', 'jquery.fileupload');
414
-		\OCP\Util::addScript('files_sharing', 'files_drop');
415
-
416
-		if (isset($shareTmpl['folder'])) {
417
-			// JS required for folders
418
-			\OCP\Util::addStyle('files', 'merged');
419
-			\OCP\Util::addScript('files', 'filesummary');
420
-			\OCP\Util::addScript('files', 'breadcrumb');
421
-			\OCP\Util::addScript('files', 'fileinfomodel');
422
-			\OCP\Util::addScript('files', 'newfilemenu');
423
-			\OCP\Util::addScript('files', 'files');
424
-			\OCP\Util::addScript('files', 'filelist');
425
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
426
-		}
427
-
428
-		// OpenGraph Support: http://ogp.me/
429
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
430
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
431
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
432
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
433
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
434
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
435
-
436
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
437
-
438
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
439
-		$csp->addAllowedFrameDomain('\'self\'');
440
-		$response = new TemplateResponse($this->appName, 'public', $shareTmpl, 'base');
441
-		$response->setContentSecurityPolicy($csp);
442
-
443
-		$this->emitAccessShareHook($share);
444
-
445
-		return $response;
446
-	}
447
-
448
-	/**
449
-	 * @PublicPage
450
-	 * @NoCSRFRequired
451
-	 *
452
-	 * @param string $token
453
-	 * @param string $files
454
-	 * @param string $path
455
-	 * @param string $downloadStartSecret
456
-	 * @return void|\OCP\AppFramework\Http\Response
457
-	 * @throws NotFoundException
458
-	 */
459
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
460
-		\OC_User::setIncognitoMode(true);
461
-
462
-		$share = $this->shareManager->getShareByToken($token);
463
-
464
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
465
-			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
466
-		}
467
-
468
-		// Share is password protected - check whether the user is permitted to access the share
469
-		if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
470
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
471
-				['token' => $token]));
472
-		}
473
-
474
-		$files_list = null;
475
-		if (!is_null($files)) { // download selected files
476
-			$files_list = json_decode($files);
477
-			// in case we get only a single file
478
-			if ($files_list === null) {
479
-				$files_list = [$files];
480
-			}
481
-			// Just in case $files is a single int like '1234'
482
-			if (!is_array($files_list)) {
483
-				$files_list = [$files_list];
484
-			}
485
-		}
486
-
487
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
488
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
489
-
490
-		if (!$this->validateShare($share)) {
491
-			throw new NotFoundException();
492
-		}
493
-
494
-		// Single file share
495
-		if ($share->getNode() instanceof \OCP\Files\File) {
496
-			// Single file download
497
-			$this->singleFileDownloaded($share, $share->getNode());
498
-		}
499
-		// Directory share
500
-		else {
501
-			/** @var \OCP\Files\Folder $node */
502
-			$node = $share->getNode();
503
-
504
-			// Try to get the path
505
-			if ($path !== '') {
506
-				try {
507
-					$node = $node->get($path);
508
-				} catch (NotFoundException $e) {
509
-					$this->emitAccessShareHook($share, 404, 'Share not found');
510
-					return new NotFoundResponse();
511
-				}
512
-			}
513
-
514
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
515
-
516
-			if ($node instanceof \OCP\Files\File) {
517
-				// Single file download
518
-				$this->singleFileDownloaded($share, $share->getNode());
519
-			} else if (!empty($files_list)) {
520
-				$this->fileListDownloaded($share, $files_list, $node);
521
-			} else {
522
-				// The folder is downloaded
523
-				$this->singleFileDownloaded($share, $share->getNode());
524
-			}
525
-		}
526
-
527
-		/* FIXME: We should do this all nicely in OCP */
528
-		OC_Util::tearDownFS();
529
-		OC_Util::setupFS($share->getShareOwner());
530
-
531
-		/**
532
-		 * this sets a cookie to be able to recognize the start of the download
533
-		 * the content must not be longer than 32 characters and must only contain
534
-		 * alphanumeric characters
535
-		 */
536
-		if (!empty($downloadStartSecret)
537
-			&& !isset($downloadStartSecret[32])
538
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
539
-
540
-			// FIXME: set on the response once we use an actual app framework response
541
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
542
-		}
543
-
544
-		$this->emitAccessShareHook($share);
545
-
546
-		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
547
-
548
-		/**
549
-		 * Http range requests support
550
-		 */
551
-		if (isset($_SERVER['HTTP_RANGE'])) {
552
-			$server_params['range'] = $this->request->getHeader('Range');
553
-		}
554
-
555
-		// download selected files
556
-		if (!is_null($files) && $files !== '') {
557
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
558
-			// after dispatching the request which results in a "Cannot modify header information" notice.
559
-			OC_Files::get($originalSharePath, $files_list, $server_params);
560
-			exit();
561
-		} else {
562
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
563
-			// after dispatching the request which results in a "Cannot modify header information" notice.
564
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
565
-			exit();
566
-		}
567
-	}
568
-
569
-	/**
570
-	 * create activity for every downloaded file
571
-	 *
572
-	 * @param Share\IShare $share
573
-	 * @param array $files_list
574
-	 * @param \OCP\Files\Folder $node
575
-	 */
576
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
577
-		foreach ($files_list as $file) {
578
-			$subNode = $node->get($file);
579
-			$this->singleFileDownloaded($share, $subNode);
580
-		}
581
-
582
-	}
583
-
584
-	/**
585
-	 * create activity if a single file was downloaded from a link share
586
-	 *
587
-	 * @param Share\IShare $share
588
-	 */
589
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
590
-
591
-		$fileId = $node->getId();
592
-
593
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
594
-		$userNodeList = $userFolder->getById($fileId);
595
-		$userNode = $userNodeList[0];
596
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
597
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
598
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
599
-
600
-		$parameters = [$userPath];
601
-
602
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
603
-			if ($node instanceof \OCP\Files\File) {
604
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
605
-			} else {
606
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
607
-			}
608
-			$parameters[] = $share->getSharedWith();
609
-		} else {
610
-			if ($node instanceof \OCP\Files\File) {
611
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
612
-			} else {
613
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
614
-			}
615
-		}
616
-
617
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
618
-
619
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
620
-			$parameters[0] = $ownerPath;
621
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
622
-		}
623
-	}
624
-
625
-	/**
626
-	 * publish activity
627
-	 *
628
-	 * @param string $subject
629
-	 * @param array $parameters
630
-	 * @param string $affectedUser
631
-	 * @param int $fileId
632
-	 * @param string $filePath
633
-	 */
634
-	protected function publishActivity($subject,
635
-										array $parameters,
636
-										$affectedUser,
637
-										$fileId,
638
-										$filePath) {
639
-
640
-		$event = $this->activityManager->generateEvent();
641
-		$event->setApp('files_sharing')
642
-			->setType('public_links')
643
-			->setSubject($subject, $parameters)
644
-			->setAffectedUser($affectedUser)
645
-			->setObject('files', $fileId, $filePath);
646
-		$this->activityManager->publish($event);
647
-	}
345
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
346
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
347
+                $freeSpace = max($freeSpace, 0);
348
+            } else {
349
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
350
+            }
351
+
352
+            $hideFileList = $share->getPermissions() & \OCP\Constants::PERMISSION_READ ? false : true;
353
+            $maxUploadFilesize = $freeSpace;
354
+
355
+            $folder = new Template('files', 'list', '');
356
+            $folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
357
+            $folder->assign('dirToken', $token);
358
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
359
+            $folder->assign('isPublic', true);
360
+            $folder->assign('hideFileList', $hideFileList);
361
+            $folder->assign('publicUploadEnabled', 'no');
362
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
363
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
364
+            $folder->assign('freeSpace', $freeSpace);
365
+            $folder->assign('usedSpacePercent', 0);
366
+            $folder->assign('trash', false);
367
+            $shareTmpl['folder'] = $folder->fetchPage();
368
+        }
369
+
370
+        $shareTmpl['hideFileList'] = $hideFileList;
371
+        $shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
372
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $token]);
373
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
374
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
375
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
376
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
377
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
378
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
379
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
380
+        $ogPreview = '';
381
+        if ($shareTmpl['previewSupported']) {
382
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
383
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
384
+            $ogPreview = $shareTmpl['previewImage'];
385
+
386
+            // We just have direct previews for image files
387
+            if ($share->getNode()->getMimePart() === 'image') {
388
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
389
+
390
+                $ogPreview = $shareTmpl['previewURL'];
391
+
392
+                //Whatapp is kind of picky about their size requirements
393
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
394
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
395
+                        't' => $token,
396
+                        'x' => 256,
397
+                        'y' => 256,
398
+                        'a' => true,
399
+                    ]);
400
+                }
401
+            }
402
+        } else {
403
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
404
+            $ogPreview = $shareTmpl['previewImage'];
405
+        }
406
+
407
+        // Load files we need
408
+        \OCP\Util::addScript('files', 'file-upload');
409
+        \OCP\Util::addStyle('files_sharing', 'publicView');
410
+        \OCP\Util::addScript('files_sharing', 'public');
411
+        \OCP\Util::addScript('files', 'fileactions');
412
+        \OCP\Util::addScript('files', 'fileactionsmenu');
413
+        \OCP\Util::addScript('files', 'jquery.fileupload');
414
+        \OCP\Util::addScript('files_sharing', 'files_drop');
415
+
416
+        if (isset($shareTmpl['folder'])) {
417
+            // JS required for folders
418
+            \OCP\Util::addStyle('files', 'merged');
419
+            \OCP\Util::addScript('files', 'filesummary');
420
+            \OCP\Util::addScript('files', 'breadcrumb');
421
+            \OCP\Util::addScript('files', 'fileinfomodel');
422
+            \OCP\Util::addScript('files', 'newfilemenu');
423
+            \OCP\Util::addScript('files', 'files');
424
+            \OCP\Util::addScript('files', 'filelist');
425
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
426
+        }
427
+
428
+        // OpenGraph Support: http://ogp.me/
429
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
430
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
431
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
432
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
433
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
434
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
435
+
436
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
437
+
438
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
439
+        $csp->addAllowedFrameDomain('\'self\'');
440
+        $response = new TemplateResponse($this->appName, 'public', $shareTmpl, 'base');
441
+        $response->setContentSecurityPolicy($csp);
442
+
443
+        $this->emitAccessShareHook($share);
444
+
445
+        return $response;
446
+    }
447
+
448
+    /**
449
+     * @PublicPage
450
+     * @NoCSRFRequired
451
+     *
452
+     * @param string $token
453
+     * @param string $files
454
+     * @param string $path
455
+     * @param string $downloadStartSecret
456
+     * @return void|\OCP\AppFramework\Http\Response
457
+     * @throws NotFoundException
458
+     */
459
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
460
+        \OC_User::setIncognitoMode(true);
461
+
462
+        $share = $this->shareManager->getShareByToken($token);
463
+
464
+        if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
465
+            return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
466
+        }
467
+
468
+        // Share is password protected - check whether the user is permitted to access the share
469
+        if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
470
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
471
+                ['token' => $token]));
472
+        }
473
+
474
+        $files_list = null;
475
+        if (!is_null($files)) { // download selected files
476
+            $files_list = json_decode($files);
477
+            // in case we get only a single file
478
+            if ($files_list === null) {
479
+                $files_list = [$files];
480
+            }
481
+            // Just in case $files is a single int like '1234'
482
+            if (!is_array($files_list)) {
483
+                $files_list = [$files_list];
484
+            }
485
+        }
486
+
487
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
488
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
489
+
490
+        if (!$this->validateShare($share)) {
491
+            throw new NotFoundException();
492
+        }
493
+
494
+        // Single file share
495
+        if ($share->getNode() instanceof \OCP\Files\File) {
496
+            // Single file download
497
+            $this->singleFileDownloaded($share, $share->getNode());
498
+        }
499
+        // Directory share
500
+        else {
501
+            /** @var \OCP\Files\Folder $node */
502
+            $node = $share->getNode();
503
+
504
+            // Try to get the path
505
+            if ($path !== '') {
506
+                try {
507
+                    $node = $node->get($path);
508
+                } catch (NotFoundException $e) {
509
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
510
+                    return new NotFoundResponse();
511
+                }
512
+            }
513
+
514
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
515
+
516
+            if ($node instanceof \OCP\Files\File) {
517
+                // Single file download
518
+                $this->singleFileDownloaded($share, $share->getNode());
519
+            } else if (!empty($files_list)) {
520
+                $this->fileListDownloaded($share, $files_list, $node);
521
+            } else {
522
+                // The folder is downloaded
523
+                $this->singleFileDownloaded($share, $share->getNode());
524
+            }
525
+        }
526
+
527
+        /* FIXME: We should do this all nicely in OCP */
528
+        OC_Util::tearDownFS();
529
+        OC_Util::setupFS($share->getShareOwner());
530
+
531
+        /**
532
+         * this sets a cookie to be able to recognize the start of the download
533
+         * the content must not be longer than 32 characters and must only contain
534
+         * alphanumeric characters
535
+         */
536
+        if (!empty($downloadStartSecret)
537
+            && !isset($downloadStartSecret[32])
538
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
539
+
540
+            // FIXME: set on the response once we use an actual app framework response
541
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
542
+        }
543
+
544
+        $this->emitAccessShareHook($share);
545
+
546
+        $server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
547
+
548
+        /**
549
+         * Http range requests support
550
+         */
551
+        if (isset($_SERVER['HTTP_RANGE'])) {
552
+            $server_params['range'] = $this->request->getHeader('Range');
553
+        }
554
+
555
+        // download selected files
556
+        if (!is_null($files) && $files !== '') {
557
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
558
+            // after dispatching the request which results in a "Cannot modify header information" notice.
559
+            OC_Files::get($originalSharePath, $files_list, $server_params);
560
+            exit();
561
+        } else {
562
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
563
+            // after dispatching the request which results in a "Cannot modify header information" notice.
564
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
565
+            exit();
566
+        }
567
+    }
568
+
569
+    /**
570
+     * create activity for every downloaded file
571
+     *
572
+     * @param Share\IShare $share
573
+     * @param array $files_list
574
+     * @param \OCP\Files\Folder $node
575
+     */
576
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
577
+        foreach ($files_list as $file) {
578
+            $subNode = $node->get($file);
579
+            $this->singleFileDownloaded($share, $subNode);
580
+        }
581
+
582
+    }
583
+
584
+    /**
585
+     * create activity if a single file was downloaded from a link share
586
+     *
587
+     * @param Share\IShare $share
588
+     */
589
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
590
+
591
+        $fileId = $node->getId();
592
+
593
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
594
+        $userNodeList = $userFolder->getById($fileId);
595
+        $userNode = $userNodeList[0];
596
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
597
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
598
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
599
+
600
+        $parameters = [$userPath];
601
+
602
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
603
+            if ($node instanceof \OCP\Files\File) {
604
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
605
+            } else {
606
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
607
+            }
608
+            $parameters[] = $share->getSharedWith();
609
+        } else {
610
+            if ($node instanceof \OCP\Files\File) {
611
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
612
+            } else {
613
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
614
+            }
615
+        }
616
+
617
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
618
+
619
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
620
+            $parameters[0] = $ownerPath;
621
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
622
+        }
623
+    }
624
+
625
+    /**
626
+     * publish activity
627
+     *
628
+     * @param string $subject
629
+     * @param array $parameters
630
+     * @param string $affectedUser
631
+     * @param int $fileId
632
+     * @param string $filePath
633
+     */
634
+    protected function publishActivity($subject,
635
+                                        array $parameters,
636
+                                        $affectedUser,
637
+                                        $fileId,
638
+                                        $filePath) {
639
+
640
+        $event = $this->activityManager->generateEvent();
641
+        $event->setApp('files_sharing')
642
+            ->setType('public_links')
643
+            ->setSubject($subject, $parameters)
644
+            ->setAffectedUser($affectedUser)
645
+            ->setObject('files', $fileId, $filePath);
646
+        $this->activityManager->publish($event);
647
+    }
648 648
 
649 649
 
650 650
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
 	public function showAuthenticate($token) {
154 154
 		$share = $this->shareManager->getShareByToken($token);
155 155
 
156
-		if($this->linkShareAuth($share)) {
156
+		if ($this->linkShareAuth($share)) {
157 157
 			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
158 158
 		}
159 159
 
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
 
182 182
 		$authenticate = $this->linkShareAuth($share, $password);
183 183
 
184
-		if($authenticate === true) {
184
+		if ($authenticate === true) {
185 185
 			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
186 186
 		}
187 187
 
@@ -205,15 +205,15 @@  discard block
 block discarded – undo
205 205
 		if ($password !== null) {
206 206
 			if ($this->shareManager->checkPassword($share, $password)) {
207 207
 				$this->session->regenerateId(true, true);
208
-				$this->session->set('public_link_authenticated', (string)$share->getId());
208
+				$this->session->set('public_link_authenticated', (string) $share->getId());
209 209
 			} else {
210 210
 				$this->emitAccessShareHook($share, 403, 'Wrong password');
211 211
 				return false;
212 212
 			}
213 213
 		} else {
214 214
 			// not authenticated ?
215
-			if ( ! $this->session->exists('public_link_authenticated')
216
-				|| $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
215
+			if (!$this->session->exists('public_link_authenticated')
216
+				|| $this->session->get('public_link_authenticated') !== (string) $share->getId()) {
217 217
 				return false;
218 218
 			}
219 219
 		}
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
 		$itemType = $itemSource = $uidOwner = '';
235 235
 		$token = $share;
236 236
 		$exception = null;
237
-		if($share instanceof \OCP\Share\IShare) {
237
+		if ($share instanceof \OCP\Share\IShare) {
238 238
 			try {
239 239
 				$token = $share->getToken();
240 240
 				$uidOwner = $share->getSharedBy();
@@ -253,7 +253,7 @@  discard block
 block discarded – undo
253 253
 			'errorCode' => $errorCode,
254 254
 			'errorMessage' => $errorMessage,
255 255
 		]);
256
-		if(!is_null($exception)) {
256
+		if (!is_null($exception)) {
257 257
 			throw $exception;
258 258
 		}
259 259
 	}
@@ -379,7 +379,7 @@  discard block
 block discarded – undo
379 379
 		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
380 380
 		$ogPreview = '';
381 381
 		if ($shareTmpl['previewSupported']) {
382
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
382
+			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
383 383
 				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
384 384
 			$ogPreview = $shareTmpl['previewImage'];
385 385
 
@@ -427,7 +427,7 @@  discard block
 block discarded – undo
427 427
 
428 428
 		// OpenGraph Support: http://ogp.me/
429 429
 		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
430
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
430
+		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName().($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : '')]);
431 431
 		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
432 432
 		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
433 433
 		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
 
462 462
 		$share = $this->shareManager->getShareByToken($token);
463 463
 
464
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
464
+		if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
465 465
 			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
466 466
 		}
467 467
 
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
 
544 544
 		$this->emitAccessShareHook($share);
545 545
 
546
-		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
546
+		$server_params = array('head' => $this->request->getMethod() === 'HEAD');
547 547
 
548 548
 		/**
549 549
 		 * Http range requests support
Please login to merge, or discard this patch.