These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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)) { |
||
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)) { |
||
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); |
||
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)) { |
||
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); |
||
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
|
|||
391 | register_pam_handler('pam_auth_userpass'); |
||
392 | }; |
||
393 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.