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 | * @since 1.9 |
||||
19 | */ |
||||
20 | function elgg_get_session() { |
||||
21 | 163 | return _elgg_services()->session; |
|||
22 | } |
||||
23 | |||||
24 | /** |
||||
25 | * Return the current logged in user, or null if no user is logged in. |
||||
26 | * |
||||
27 | * @return \ElggUser|null |
||||
28 | */ |
||||
29 | function elgg_get_logged_in_user_entity() { |
||||
30 | 260 | 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 | * @return int |
||||
38 | */ |
||||
39 | function elgg_get_logged_in_user_guid() { |
||||
40 | 1255 | return _elgg_services()->session->getLoggedInUserGuid(); |
|||
41 | } |
||||
42 | |||||
43 | /** |
||||
44 | * Returns whether or not the user is currently logged in |
||||
45 | * |
||||
46 | * @return bool |
||||
47 | */ |
||||
48 | function elgg_is_logged_in() { |
||||
49 | 5389 | 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 | * @return bool |
||||
56 | */ |
||||
57 | function elgg_is_admin_logged_in() { |
||||
58 | 39 | 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 | */ |
||||
71 | function elgg_is_admin_user($user_guid) { |
||||
72 | |||||
73 | $user_guid = (int) $user_guid; |
||||
74 | |||||
75 | $entity = get_user($user_guid); |
||||
76 | if (!$entity) { |
||||
77 | return false; |
||||
78 | } |
||||
79 | |||||
80 | return $entity->isAdmin(); |
||||
81 | } |
||||
82 | |||||
83 | /** |
||||
84 | * Perform user authentication with a given username and password. |
||||
85 | * |
||||
86 | * @warning This returns an error message on failure. Use the identical operator to check |
||||
87 | * for access: if (true === elgg_authenticate()) { ... }. |
||||
88 | * |
||||
89 | * |
||||
90 | * @see login |
||||
91 | * |
||||
92 | * @param string $username The username |
||||
93 | * @param string $password The password |
||||
94 | * |
||||
95 | * @return true|string True or an error message on failure |
||||
96 | * @access private |
||||
97 | */ |
||||
98 | function elgg_authenticate($username, $password) { |
||||
99 | 7 | $pam = new \ElggPAM('user'); |
|||
100 | 7 | $credentials = ['username' => $username, 'password' => $password]; |
|||
101 | 7 | $result = $pam->authenticate($credentials); |
|||
102 | 7 | if (!$result) { |
|||
103 | 2 | return $pam->getFailureMessage(); |
|||
104 | } |
||||
105 | 5 | return true; |
|||
106 | } |
||||
107 | |||||
108 | /** |
||||
109 | * Hook into the PAM system which accepts a username and password and attempts to authenticate |
||||
110 | * it against a known user. |
||||
111 | * |
||||
112 | * @param array $credentials Associated array of credentials passed to |
||||
113 | * Elgg's PAM system. This function expects |
||||
114 | * 'username' and 'password' (cleartext). |
||||
115 | * |
||||
116 | * @return bool |
||||
117 | * @throws LoginException |
||||
118 | * @access private |
||||
119 | */ |
||||
120 | function pam_auth_userpass(array $credentials = []) { |
||||
121 | |||||
122 | 9 | if (!isset($credentials['username']) || !isset($credentials['password'])) { |
|||
123 | 2 | return false; |
|||
124 | } |
||||
125 | |||||
126 | 7 | $user = get_user_by_username($credentials['username']); |
|||
127 | 7 | if (!$user) { |
|||
128 | 1 | throw new \LoginException(_elgg_services()->translator->translate('LoginException:UsernameFailure')); |
|||
129 | } |
||||
130 | |||||
131 | 6 | $password_svc = _elgg_services()->passwords; |
|||
132 | 6 | $password = $credentials['password']; |
|||
133 | 6 | $hash = $user->password_hash; |
|||
134 | |||||
135 | 6 | if (check_rate_limit_exceeded($user->guid)) { |
|||
136 | throw new \LoginException(_elgg_services()->translator->translate('LoginException:AccountLocked')); |
||||
137 | } |
||||
138 | |||||
139 | 6 | if (!$password_svc->verify($password, $hash)) { |
|||
140 | 1 | log_login_failure($user->guid); |
|||
141 | 1 | throw new \LoginException(_elgg_services()->translator->translate('LoginException:PasswordFailure')); |
|||
142 | } |
||||
143 | |||||
144 | 5 | if ($password_svc->needsRehash($hash)) { |
|||
145 | $password_svc->forcePasswordReset($user, $password); |
||||
146 | } |
||||
147 | |||||
148 | 5 | return true; |
|||
149 | } |
||||
150 | |||||
151 | /** |
||||
152 | * Log a failed login for $user_guid |
||||
153 | * |
||||
154 | * @param int $user_guid User GUID |
||||
155 | * |
||||
156 | * @return bool |
||||
157 | */ |
||||
158 | function log_login_failure($user_guid) { |
||||
159 | 1 | $user_guid = (int) $user_guid; |
|||
160 | 1 | $user = get_entity($user_guid); |
|||
161 | |||||
162 | 1 | if (($user_guid) && ($user) && ($user instanceof \ElggUser)) { |
|||
163 | 1 | $fails = (int) $user->getPrivateSetting("login_failures"); |
|||
164 | 1 | $fails++; |
|||
165 | |||||
166 | 1 | $user->setPrivateSetting("login_failures", $fails); |
|||
167 | 1 | $user->setPrivateSetting("login_failure_$fails", time()); |
|||
168 | 1 | return true; |
|||
169 | } |
||||
170 | |||||
171 | return false; |
||||
172 | } |
||||
173 | |||||
174 | /** |
||||
175 | * Resets the fail login count for $user_guid |
||||
176 | * |
||||
177 | * @param int $user_guid User GUID |
||||
178 | * |
||||
179 | * @return bool true on success (success = user has no logged failed attempts) |
||||
180 | */ |
||||
181 | function reset_login_failure_count($user_guid) { |
||||
182 | 4 | $user_guid = (int) $user_guid; |
|||
183 | 4 | $user = get_entity($user_guid); |
|||
184 | |||||
185 | 4 | if (($user_guid) && ($user) && ($user instanceof \ElggUser)) { |
|||
186 | 4 | $fails = (int) $user->getPrivateSetting("login_failures"); |
|||
187 | |||||
188 | 4 | if ($fails) { |
|||
189 | for ($n = 1; $n <= $fails; $n++) { |
||||
190 | $user->removePrivateSetting("login_failure_$n"); |
||||
191 | } |
||||
192 | |||||
193 | $user->removePrivateSetting("login_failures"); |
||||
194 | |||||
195 | return true; |
||||
196 | } |
||||
197 | |||||
198 | // nothing to reset |
||||
199 | 4 | return true; |
|||
200 | } |
||||
201 | |||||
202 | return false; |
||||
203 | } |
||||
204 | |||||
205 | /** |
||||
206 | * Checks if the rate limit of failed logins has been exceeded for $user_guid. |
||||
207 | * |
||||
208 | * @param int $user_guid User GUID |
||||
209 | * |
||||
210 | * @return bool on exceeded limit. |
||||
211 | */ |
||||
212 | function check_rate_limit_exceeded($user_guid) { |
||||
213 | // 5 failures in 5 minutes causes temporary block on logins |
||||
214 | 6 | $limit = 5; |
|||
215 | 6 | $user_guid = (int) $user_guid; |
|||
216 | 6 | $user = get_entity($user_guid); |
|||
217 | |||||
218 | 6 | if (($user_guid) && ($user) && ($user instanceof \ElggUser)) { |
|||
219 | 6 | $fails = (int) $user->getPrivateSetting("login_failures"); |
|||
220 | 6 | if ($fails >= $limit) { |
|||
221 | $cnt = 0; |
||||
222 | $time = time(); |
||||
223 | for ($n = $fails; $n > 0; $n--) { |
||||
224 | $f = $user->getPrivateSetting("login_failure_$n"); |
||||
225 | if ($f > $time - (60 * 5)) { |
||||
226 | $cnt++; |
||||
227 | } |
||||
228 | |||||
229 | if ($cnt == $limit) { |
||||
230 | // Limit reached |
||||
231 | return true; |
||||
232 | } |
||||
233 | } |
||||
234 | } |
||||
235 | } |
||||
236 | |||||
237 | 6 | return false; |
|||
238 | } |
||||
239 | |||||
240 | /** |
||||
241 | * Set a cookie, but allow plugins to customize it first. |
||||
242 | * |
||||
243 | * To customize all cookies, register for the 'init:cookie', 'all' event. |
||||
244 | * |
||||
245 | * @param \ElggCookie $cookie The cookie that is being set |
||||
246 | * @return bool |
||||
247 | * @since 1.9 |
||||
248 | */ |
||||
249 | function elgg_set_cookie(\ElggCookie $cookie) { |
||||
250 | if (elgg_trigger_event('init:cookie', $cookie->name, $cookie)) { |
||||
251 | return setcookie($cookie->name, $cookie->value, $cookie->expire, $cookie->path, |
||||
252 | $cookie->domain, $cookie->secure, $cookie->httpOnly); |
||||
253 | } |
||||
254 | return false; |
||||
255 | } |
||||
256 | |||||
257 | /** |
||||
258 | * Logs in a specified \ElggUser. For standard registration, use in conjunction |
||||
259 | * with elgg_authenticate. |
||||
260 | * |
||||
261 | * @see elgg_authenticate |
||||
262 | * |
||||
263 | * @param \ElggUser $user A valid Elgg user object |
||||
264 | * @param boolean $persistent Should this be a persistent login? |
||||
265 | * |
||||
266 | * @return true or throws exception |
||||
267 | * @throws LoginException |
||||
268 | */ |
||||
269 | function login(\ElggUser $user, $persistent = false) { |
||||
270 | 6 | if ($user->isBanned()) { |
|||
271 | 1 | throw new \LoginException(elgg_echo('LoginException:BannedUser')); |
|||
272 | } |
||||
273 | |||||
274 | 5 | $session = _elgg_services()->session; |
|||
275 | |||||
276 | // give plugins a chance to reject the login of this user (no user in session!) |
||||
277 | 5 | if (!elgg_trigger_before_event('login', 'user', $user)) { |
|||
278 | 1 | throw new \LoginException(elgg_echo('LoginException:Unknown')); |
|||
279 | } |
||||
280 | |||||
281 | // #5933: set logged in user early so code in login event will be able to |
||||
282 | // use elgg_get_logged_in_user_entity(). |
||||
283 | 4 | $session->setLoggedInUser($user); |
|||
284 | |||||
285 | // if remember me checked, set cookie with token and store hash(token) for user |
||||
286 | 4 | if ($persistent) { |
|||
287 | _elgg_services()->persistentLogin->makeLoginPersistent($user); |
||||
288 | } |
||||
289 | |||||
290 | // User's privilege has been elevated, so change the session id (prevents session fixation) |
||||
291 | 4 | $session->migrate(); |
|||
292 | |||||
293 | 4 | $user->setLastLogin(); |
|||
294 | 4 | reset_login_failure_count($user->guid); |
|||
295 | |||||
296 | 4 | elgg_trigger_after_event('login', 'user', $user); |
|||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
297 | |||||
298 | 4 | return true; |
|||
299 | } |
||||
300 | |||||
301 | /** |
||||
302 | * Log the current user out |
||||
303 | * |
||||
304 | * @return bool |
||||
305 | */ |
||||
306 | function logout() { |
||||
307 | $session = _elgg_services()->session; |
||||
308 | $user = $session->getLoggedInUser(); |
||||
309 | if (!$user) { |
||||
310 | return false; |
||||
311 | } |
||||
312 | |||||
313 | if (!elgg_trigger_before_event('logout', 'user', $user)) { |
||||
314 | return false; |
||||
315 | } |
||||
316 | |||||
317 | _elgg_services()->persistentLogin->removePersistentLogin(); |
||||
318 | |||||
319 | // pass along any messages into new session |
||||
320 | $old_msg = $session->get(SystemMessagesService::SESSION_KEY, []); |
||||
321 | $session->invalidate(); |
||||
322 | $session->set(SystemMessagesService::SESSION_KEY, $old_msg); |
||||
323 | |||||
324 | elgg_trigger_after_event('logout', 'user', $user); |
||||
0 ignored issues
–
show
$user of type ElggUser is incompatible with the type string expected by parameter $object of elgg_trigger_after_event() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
325 | |||||
326 | return true; |
||||
327 | } |
||||
328 | |||||
329 | /** |
||||
330 | * Initializes the session and checks for the remember me cookie |
||||
331 | * |
||||
332 | * @param ServiceProvider $services Services |
||||
333 | * @return bool |
||||
334 | * @throws SecurityException |
||||
335 | * @access private |
||||
336 | */ |
||||
337 | function _elgg_session_boot(ServiceProvider $services) { |
||||
338 | 4777 | $services->timer->begin([__FUNCTION__]); |
|||
339 | |||||
340 | 4777 | $session = $services->session; |
|||
341 | 4777 | $session->start(); |
|||
342 | |||||
343 | // test whether we have a user session |
||||
344 | 4777 | if ($session->has('guid')) { |
|||
345 | /** @var ElggUser $user */ |
||||
346 | $user = $services->entityTable->get($session->get('guid'), 'user'); |
||||
347 | if (!$user) { |
||||
348 | // OMG user has been deleted. |
||||
349 | $session->invalidate(); |
||||
350 | forward(''); |
||||
351 | } |
||||
352 | |||||
353 | $services->persistentLogin->replaceLegacyToken($user); |
||||
354 | } else { |
||||
355 | 4777 | $user = $services->persistentLogin->bootSession(); |
|||
0 ignored issues
–
show
Are you sure the assignment to
$user is correct as $services->persistentLogin->bootSession() targeting Elgg\PersistentLoginService::bootSession() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
356 | } |
||||
357 | |||||
358 | 4777 | if ($user) { |
|||
359 | $session->setLoggedInUser($user); |
||||
0 ignored issues
–
show
$user of type void is incompatible with the type ElggUser expected by parameter $user of ElggSession::setLoggedInUser() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
360 | $user->setLastAction(); |
||||
361 | |||||
362 | // logout a user with open session who has been banned |
||||
363 | if ($user->isBanned()) { |
||||
364 | logout(); |
||||
365 | return false; |
||||
366 | } |
||||
367 | } |
||||
368 | |||||
369 | 4777 | $services->timer->end([__FUNCTION__]); |
|||
370 | 4777 | return true; |
|||
371 | } |
||||
372 | |||||
373 | /** |
||||
374 | * @see \Elgg\Application::loadCore Do not do work here. Just register for events. |
||||
375 | */ |
||||
376 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
||||
377 | 18 | register_pam_handler('pam_auth_userpass'); |
|||
378 | }; |
||||
379 |