Test Failed
Push — master ( c2873c...a077d1 )
by Jeroen
01:35
created

sessions.php ➔ _elgg_session_boot()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 20
nc 9
nop 1
dl 0
loc 35
ccs 0
cts 18
cp 0
crap 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
use Elgg\SystemMessagesService;
4
use Elgg\Di\ServiceProvider;
5
6
/**
7
 * Elgg session management
8
 * Functions to manage logins
9
 *
10
 * @package    Elgg.Core
11
 * @subpackage Session
12
 */
13
14
/**
15
 * Gets Elgg's session object
16
 *
17
 * @return \ElggSession
18 119
 * @since 1.9
19
 */
20
function elgg_get_session() {
21
	return _elgg_services()->session;
22
}
23
24
/**
25
 * Return the current logged in user, or null if no user is logged in.
26
 *
27 261
 * @return \ElggUser|null
28
 */
29
function elgg_get_logged_in_user_entity() {
30
	return _elgg_services()->session->getLoggedInUser();
31
}
32
33
/**
34
 * Return the current logged in user by guid.
35
 *
36
 * @see elgg_get_logged_in_user_entity()
37 262
 * @return int
38
 */
39
function elgg_get_logged_in_user_guid() {
40
	return _elgg_services()->session->getLoggedInUserGuid();
41
}
42
43
/**
44
 * Returns whether or not the user is currently logged in
45
 *
46 4
 * @return bool
47
 */
48
function elgg_is_logged_in() {
49
	return _elgg_services()->session->isLoggedIn();
50
}
51
52
/**
53
 * Returns whether or not the viewer is currently logged in and an admin user.
54
 *
55 4
 * @return bool
56
 */
57
function elgg_is_admin_logged_in() {
58
	return _elgg_services()->session->isAdminLoggedIn();
59
}
60
61
/**
62
 * Check if the given user has full access.
63
 *
64
 * @todo: Will always return full access if the user is an admin.
65
 *
66
 * @param int $user_guid The user to check
67
 *
68
 * @return bool
69
 * @since 1.7.1
70 261
 */
71
function elgg_is_admin_user($user_guid) {
72 261
	
73 261
	$user_guid = (int) $user_guid;
74 24
75
	$current_user = elgg_get_logged_in_user_entity();
76
	if ($current_user && $current_user->guid == $user_guid) {
77
		return $current_user->isAdmin();
78 261
	}
79 261
80
	// cannot use magic metadata here because of recursion
81 261
	$dbprefix = _elgg_config()->dbprefix;
82
	$query = "SELECT 1 FROM {$dbprefix}users_entity as e
83
		WHERE (
84
			e.guid = {$user_guid}
85
			AND e.admin = 'yes'
86
		)";
87 261
88 261
	// normalizing the results from get_data()
89
	// See #1242
90
	$info = get_data($query);
91 261
	if (!((is_array($info) && count($info) < 1) || $info === false)) {
92
		return true;
93
	}
94
	return false;
95
}
96
97
/**
98
 * Perform user authentication with a given username and password.
99
 *
100
 * @warning This returns an error message on failure. Use the identical operator to check
101
 * for access: if (true === elgg_authenticate()) { ... }.
102
 *
103
 *
104
 * @see login
105
 *
106
 * @param string $username The username
107
 * @param string $password The password
108
 *
109
 * @return true|string True or an error message on failure
110
 * @access private
111
 */
112
function elgg_authenticate($username, $password) {
113
	$pam = new \ElggPAM('user');
114
	$credentials = ['username' => $username, 'password' => $password];
115
	$result = $pam->authenticate($credentials);
116
	if (!$result) {
117
		return $pam->getFailureMessage();
118
	}
119
	return true;
120
}
121
122
/**
123
 * Hook into the PAM system which accepts a username and password and attempts to authenticate
124
 * it against a known user.
125
 *
126
 * @param array $credentials Associated array of credentials passed to
127
 *                           Elgg's PAM system. This function expects
128
 *                           'username' and 'password' (cleartext).
129
 *
130
 * @return bool
131
 * @throws LoginException
132
 * @access private
133
 */
134
function pam_auth_userpass(array $credentials = []) {
135
136
	if (!isset($credentials['username']) || !isset($credentials['password'])) {
137
		return false;
138
	}
139
140
	$user = get_user_by_username($credentials['username']);
141
	if (!$user) {
142
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:UsernameFailure'));
143
	}
144
145
	$password_svc = _elgg_services()->passwords;
146
	$password = $credentials['password'];
147
	$hash = $user->password_hash;
148
149
	if (check_rate_limit_exceeded($user->guid)) {
150
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:AccountLocked'));
151
	}
152
153
	if (!$password_svc->verify($password, $hash)) {
154
		log_login_failure($user->guid);
155
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:PasswordFailure'));
156
	}
157
158
	if ($password_svc->needsRehash($hash)) {
159
		$password_svc->forcePasswordReset($user, $password);
160
	}
161
162
	return true;
163
}
164
165
/**
166
 * Log a failed login for $user_guid
167
 *
168
 * @param int $user_guid User GUID
169
 *
170
 * @return bool
171
 */
172
function log_login_failure($user_guid) {
173
	$user_guid = (int) $user_guid;
174
	$user = get_entity($user_guid);
175
176
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
177
		$fails = (int) $user->getPrivateSetting("login_failures");
178
		$fails++;
179
180
		$user->setPrivateSetting("login_failures", $fails);
181
		$user->setPrivateSetting("login_failure_$fails", time());
182
		return true;
183
	}
184
185
	return false;
186
}
187
188
/**
189
 * Resets the fail login count for $user_guid
190
 *
191
 * @param int $user_guid User GUID
192
 *
193
 * @return bool true on success (success = user has no logged failed attempts)
194
 */
195
function reset_login_failure_count($user_guid) {
196
	$user_guid = (int) $user_guid;
197
	$user = get_entity($user_guid);
198
199
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
200
		$fails = (int) $user->getPrivateSetting("login_failures");
201
202
		if ($fails) {
203
			for ($n = 1; $n <= $fails; $n++) {
204
				$user->removePrivateSetting("login_failure_$n");
205
			}
206
207
			$user->removePrivateSetting("login_failures");
208
209
			return true;
210
		}
211
212
		// nothing to reset
213
		return true;
214
	}
215
216
	return false;
217
}
218
219
/**
220
 * Checks if the rate limit of failed logins has been exceeded for $user_guid.
221
 *
222
 * @param int $user_guid User GUID
223
 *
224
 * @return bool on exceeded limit.
225
 */
226
function check_rate_limit_exceeded($user_guid) {
227
	// 5 failures in 5 minutes causes temporary block on logins
228
	$limit = 5;
229
	$user_guid = (int) $user_guid;
230
	$user = get_entity($user_guid);
231
232
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
233
		$fails = (int) $user->getPrivateSetting("login_failures");
234
		if ($fails >= $limit) {
235
			$cnt = 0;
236
			$time = time();
237
			for ($n = $fails; $n > 0; $n--) {
238
				$f = $user->getPrivateSetting("login_failure_$n");
239
				if ($f > $time - (60 * 5)) {
240
					$cnt++;
241
				}
242
243
				if ($cnt == $limit) {
244
					// Limit reached
245
					return true;
246
				}
247
			}
248
		}
249
	}
250
251
	return false;
252
}
253
254
/**
255
 * Set a cookie, but allow plugins to customize it first.
256
 *
257
 * To customize all cookies, register for the 'init:cookie', 'all' event.
258
 *
259
 * @param \ElggCookie $cookie The cookie that is being set
260
 * @return bool
261
 * @since 1.9
262
 */
263
function elgg_set_cookie(\ElggCookie $cookie) {
264
	if (elgg_trigger_event('init:cookie', $cookie->name, $cookie)) {
0 ignored issues
show
Documentation introduced by
$cookie is of type object<ElggCookie>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
265
		return setcookie($cookie->name, $cookie->value, $cookie->expire, $cookie->path,
266
						$cookie->domain, $cookie->secure, $cookie->httpOnly);
267
	}
268
	return false;
269
}
270
271
/**
272
 * Logs in a specified \ElggUser. For standard registration, use in conjunction
273
 * with elgg_authenticate.
274
 *
275
 * @see elgg_authenticate
276
 *
277
 * @param \ElggUser $user       A valid Elgg user object
278
 * @param boolean   $persistent Should this be a persistent login?
279
 *
280
 * @return true or throws exception
281
 * @throws LoginException
282
 */
283
function login(\ElggUser $user, $persistent = false) {
284
	if ($user->isBanned()) {
285
		throw new \LoginException(elgg_echo('LoginException:BannedUser'));
286
	}
287
288
	$session = _elgg_services()->session;
289
290
	// give plugins a chance to reject the login of this user (no user in session!)
291
	if (!elgg_trigger_before_event('login', 'user', $user)) {
0 ignored issues
show
Documentation introduced by
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
292
		throw new \LoginException(elgg_echo('LoginException:Unknown'));
293
	}
294
295
	// #5933: set logged in user early so code in login event will be able to
296
	// use elgg_get_logged_in_user_entity().
297
	$session->setLoggedInUser($user);
298
299
	// if remember me checked, set cookie with token and store hash(token) for user
300
	if ($persistent) {
301
		_elgg_services()->persistentLogin->makeLoginPersistent($user);
302
	}
303
304
	// User's privilege has been elevated, so change the session id (prevents session fixation)
305
	$session->migrate();
306
307
	set_last_login($user->guid);
308
	reset_login_failure_count($user->guid);
309
310
	elgg_trigger_after_event('login', 'user', $user);
0 ignored issues
show
Documentation introduced by
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
311
312
	return true;
313
}
314
315
/**
316
 * Log the current user out
317
 *
318
 * @return bool
319
 */
320
function logout() {
321
	$session = _elgg_services()->session;
322
	$user = $session->getLoggedInUser();
323
	if (!$user) {
324
		return false;
325
	}
326
327
	if (!elgg_trigger_before_event('logout', 'user', $user)) {
0 ignored issues
show
Documentation introduced by
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
328
		return false;
329
	}
330
331
	_elgg_services()->persistentLogin->removePersistentLogin();
332
333
	// pass along any messages into new session
334
	$old_msg = $session->get(SystemMessagesService::SESSION_KEY, []);
335
	$session->invalidate();
336
	$session->set(SystemMessagesService::SESSION_KEY, $old_msg);
337
338
	elgg_trigger_after_event('logout', 'user', $user);
0 ignored issues
show
Documentation introduced by
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
339
340
	return true;
341
}
342
343
/**
344
 * Initializes the session and checks for the remember me cookie
345
 *
346
 * @param ServiceProvider $services Services
347
 * @return bool
348
 * @throws SecurityException
349
 * @access private
350
 */
351
function _elgg_session_boot(ServiceProvider $services) {
352
	$services->timer->begin([__FUNCTION__]);
353
354
	$session = $services->session;
355
	$session->start();
356
357
	// test whether we have a user session
358
	if ($session->has('guid')) {
359
		/** @var ElggUser $user */
360
		$user = $services->entityTable->get($session->get('guid'), 'user');
361
		if (!$user) {
362
			// OMG user has been deleted.
363
			$session->invalidate();
364
			forward('');
365
		}
366
367
		$services->persistentLogin->replaceLegacyToken($user);
368
	} else {
369
		$user = $services->persistentLogin->bootSession();
370
	}
371
372
	if ($user) {
373
		$session->setLoggedInUser($user);
374
		set_last_action($user);
375
376
		// logout a user with open session who has been banned
377
		if ($user->isBanned()) {
378
			logout();
379
			return false;
380
		}
381
	}
382
383
	$services->timer->end([__FUNCTION__]);
384
	return true;
385
}
386
387
/**
388
 * @see \Elgg\Application::loadCore Do not do work here. Just register for events.
389
 */
390
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
0 ignored issues
show
Unused Code introduced by
The parameter $events is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $hooks is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
391
	register_pam_handler('pam_auth_userpass');
392
};
393