aboutsummaryrefslogtreecommitdiff
path: root/engine/lib/sessions.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/lib/sessions.php')
-rw-r--r--engine/lib/sessions.php438
1 files changed, 175 insertions, 263 deletions
diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php
index f4b1fc69b..e3d5ce9cd 100644
--- a/engine/lib/sessions.php
+++ b/engine/lib/sessions.php
@@ -4,137 +4,43 @@
* Elgg session management
* Functions to manage logins
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Session
*/
/** Elgg magic session */
global $SESSION;
/**
- * Magic session class.
- * This class is intended to extend the $_SESSION magic variable by providing an API hook
- * to plug in other values.
+ * Return the current logged in user, or NULL if no user is logged in.
*
- * Primarily this is intended to provide a way of supplying "logged in user" details without touching the session
- * (which can cause problems when accessed server side).
+ * If no user can be found in the current session, a plugin
+ * hook - 'session:get' 'user' to give plugin authors another
+ * way to provide user details to the ACL system without touching the session.
*
- * If a value is present in the session then that value is returned, otherwise a plugin hook 'session:get', '$var' is called,
- * where $var is the variable being requested.
- *
- * Setting values will store variables in the session in the normal way.
- *
- * LIMITATIONS: You can not access multidimensional arrays
- *
- * This is EXPERIMENTAL.
- */
-class ElggSession implements ArrayAccess {
- /** Local cache of trigger retrieved variables */
- private static $__localcache;
-
- function __isset($key) {
- return $this->offsetExists($key);
- }
-
- /** Set a value, go straight to session. */
- function offsetSet($key, $value) {
- $_SESSION[$key] = $value;
- }
-
- /**
- * Get a variable from either the session, or if its not in the session attempt to get it from
- * an api call.
- */
- function offsetGet($key) {
- if (!ElggSession::$__localcache) {
- ElggSession::$__localcache = array();
- }
-
- if (isset($_SESSION[$key])) {
- return $_SESSION[$key];
- }
-
- if (isset(ElggSession::$__localcache[$key])) {
- return ElggSession::$__localcache[$key];
- }
-
- $value = null;
- $value = trigger_plugin_hook('session:get', $key, null, $value);
-
- ElggSession::$__localcache[$key] = $value;
-
- return ElggSession::$__localcache[$key];
- }
-
- /**
- * Unset a value from the cache and the session.
- */
- function offsetUnset($key) {
- unset(ElggSession::$__localcache[$key]);
- unset($_SESSION[$key]);
- }
-
- /**
- * Return whether the value is set in either the session or the cache.
- */
- function offsetExists($offset) {
- if (isset(ElggSession::$__localcache[$offset])) {
- return true;
- }
-
- if (isset($_SESSION[$offset])) {
- return true;
- }
-
- if ($this->offsetGet($offset)){
- return true;
- }
- }
-
-
- // Alias functions
- function get($key) {
- return $this->offsetGet($key);
- }
-
- function set($key, $value) {
- return $this->offsetSet($key, $value);
- }
-
- function del($key) {
- return $this->offsetUnset($key);
- }
-}
-
-
-/**
- * Return the current logged in user, or null if no user is logged in.
- *
- * If no user can be found in the current session, a plugin hook - 'session:get' 'user' to give plugin
- * authors another way to provide user details to the ACL system without touching the session.
+ * @return ElggUser
*/
-function get_loggedin_user() {
+function elgg_get_logged_in_user_entity() {
global $SESSION;
if (isset($SESSION)) {
return $SESSION['user'];
}
- return false;
+ return NULL;
}
/**
* Return the current logged in user by id.
*
- * @see get_loggedin_user()
+ * @see elgg_get_logged_in_user_entity()
* @return int
*/
-function get_loggedin_userid() {
- $user = get_loggedin_user();
- if ($user)
+function elgg_get_logged_in_user_guid() {
+ $user = elgg_get_logged_in_user_entity();
+ if ($user) {
return $user->guid;
+ }
return 0;
}
@@ -142,14 +48,10 @@ function get_loggedin_userid() {
/**
* Returns whether or not the user is currently logged in
*
- * @return true|false
+ * @return bool
*/
-function isloggedin() {
- if (!is_installed()) {
- return false;
- }
-
- $user = get_loggedin_user();
+function elgg_is_logged_in() {
+ $user = elgg_get_logged_in_user_entity();
if ((isset($user)) && ($user instanceof ElggUser) && ($user->guid > 0)) {
return true;
@@ -161,17 +63,12 @@ function isloggedin() {
/**
* Returns whether or not the user is currently logged in and that they are an admin user.
*
- * @uses isloggedin()
- * @return true|false
+ * @return bool
*/
-function isadminloggedin() {
- if (!is_installed()) {
- return FALSE;
- }
-
- $user = get_loggedin_user();
+function elgg_is_admin_logged_in() {
+ $user = elgg_get_logged_in_user_entity();
- if ((isloggedin()) && $user->isAdmin()) {
+ if ((elgg_is_logged_in()) && $user->isAdmin()) {
return TRUE;
}
@@ -180,13 +77,19 @@ function isadminloggedin() {
/**
* Check if the given user has full access.
+ *
* @todo: Will always return full access if the user is an admin.
*
- * @param $user_guid
+ * @param int $user_guid The user to check
+ *
* @return bool
+ * @since 1.7.1
*/
function elgg_is_admin_user($user_guid) {
global $CONFIG;
+
+ $user_guid = (int)$user_guid;
+
// cannot use magic metadata here because of recursion
// must support the old way of getting admin from metadata
@@ -225,63 +128,71 @@ function elgg_is_admin_user($user_guid) {
}
/**
- * Perform standard authentication with a given username and password.
- * Returns an ElggUser object for use with login.
+ * Perform user authentication with a given username and password.
+ *
+ * @warning This returns an error message on failure. Use the identical operator to check
+ * for access: if (true === elgg_authenticate()) { ... }.
+ *
*
* @see login
- * @param string $username The username, optionally (for standard logins)
- * @param string $password The password, optionally (for standard logins)
- * @return ElggUser|false The authenticated user object, or false on failure.
+ *
+ * @param string $username The username
+ * @param string $password The password
+ *
+ * @return true|string True or an error message on failure
+ * @access private
*/
-
-function authenticate($username, $password) {
- if (pam_authenticate(array('username' => $username, 'password' => $password))) {
- return get_user_by_username($username);
+function elgg_authenticate($username, $password) {
+ $pam = new ElggPAM('user');
+ $credentials = array('username' => $username, 'password' => $password);
+ $result = $pam->authenticate($credentials);
+ if (!$result) {
+ return $pam->getFailureMessage();
}
-
- return false;
+ return true;
}
/**
* Hook into the PAM system which accepts a username and password and attempts to authenticate
* it against a known user.
*
- * @param array $credentials Associated array of credentials passed to pam_authenticate. This function expects
- * 'username' and 'password' (cleartext).
+ * @param array $credentials Associated array of credentials passed to
+ * Elgg's PAM system. This function expects
+ * 'username' and 'password' (cleartext).
+ *
+ * @return bool
+ * @throws LoginException
+ * @access private
*/
-function pam_auth_userpass($credentials = NULL) {
+function pam_auth_userpass(array $credentials = array()) {
- if (is_array($credentials) && ($credentials['username']) && ($credentials['password'])) {
- if ($user = get_user_by_username($credentials['username'])) {
-
- // Let admins log in without validating their email, but normal users must have validated their email or been admin created
- if ((!$user->isAdmin()) && (!$user->validated) && (!$user->admin_created)) {
- return false;
- }
+ if (!isset($credentials['username']) || !isset($credentials['password'])) {
+ return false;
+ }
- // User has been banned, so prevent from logging in
- if ($user->isBanned()) {
- return false;
- }
+ $user = get_user_by_username($credentials['username']);
+ if (!$user) {
+ throw new LoginException(elgg_echo('LoginException:UsernameFailure'));
+ }
- if ($user->password == generate_user_password($user, $credentials['password'])) {
- return true;
- } else {
- // Password failed, log.
- log_login_failure($user->guid);
- }
+ if (check_rate_limit_exceeded($user->guid)) {
+ throw new LoginException(elgg_echo('LoginException:AccountLocked'));
+ }
- }
+ if ($user->password !== generate_user_password($user, $credentials['password'])) {
+ log_login_failure($user->guid);
+ throw new LoginException(elgg_echo('LoginException:PasswordFailure'));
}
- return false;
+ return true;
}
/**
* Log a failed login for $user_guid
*
- * @param $user_guid
- * @return bool on success
+ * @param int $user_guid User GUID
+ *
+ * @return bool
*/
function log_login_failure($user_guid) {
$user_guid = (int)$user_guid;
@@ -302,8 +213,9 @@ function log_login_failure($user_guid) {
/**
* Resets the fail login count for $user_guid
*
- * @param $user_guid
- * @return bool on success (success = user has no logged failed attempts)
+ * @param int $user_guid User GUID
+ *
+ * @return bool true on success (success = user has no logged failed attempts)
*/
function reset_login_failure_count($user_guid) {
$user_guid = (int)$user_guid;
@@ -313,7 +225,7 @@ function reset_login_failure_count($user_guid) {
$fails = (int)$user->getPrivateSetting("login_failures");
if ($fails) {
- for ($n=1; $n <= $fails; $n++) {
+ for ($n = 1; $n <= $fails; $n++) {
$user->removePrivateSetting("login_failure_$n");
}
@@ -332,7 +244,8 @@ function reset_login_failure_count($user_guid) {
/**
* Checks if the rate limit of failed logins has been exceeded for $user_guid.
*
- * @param $user_guid
+ * @param int $user_guid User GUID
+ *
* @return bool on exceeded limit.
*/
function check_rate_limit_exceeded($user_guid) {
@@ -346,13 +259,13 @@ function check_rate_limit_exceeded($user_guid) {
if ($fails >= $limit) {
$cnt = 0;
$time = time();
- for ($n=$fails; $n>0; $n--) {
+ for ($n = $fails; $n > 0; $n--) {
$f = $user->getPrivateSetting("login_failure_$n");
- if ($f > $time - (60*5)) {
+ if ($f > $time - (60 * 5)) {
$cnt++;
}
- if ($cnt==$limit) {
+ if ($cnt == $limit) {
// Limit reached
return true;
}
@@ -365,24 +278,20 @@ function check_rate_limit_exceeded($user_guid) {
/**
* Logs in a specified ElggUser. For standard registration, use in conjunction
- * with authenticate.
+ * with elgg_authenticate.
+ *
+ * @see elgg_authenticate
*
- * @see authenticate
- * @param ElggUser $user A valid Elgg user object
- * @param boolean $persistent Should this be a persistent login?
- * @return true|false Whether login was successful
+ * @param ElggUser $user A valid Elgg user object
+ * @param boolean $persistent Should this be a persistent login?
+ *
+ * @return true or throws exception
+ * @throws LoginException
*/
function login(ElggUser $user, $persistent = false) {
- global $CONFIG;
-
// User is banned, return false.
if ($user->isBanned()) {
- return false;
- }
-
- // Check rate limit
- if (check_rate_limit_exceeded($user->guid)) {
- return false;
+ throw new LoginException(elgg_echo('LoginException:BannedUser'));
}
$_SESSION['user'] = $user;
@@ -396,18 +305,18 @@ function login(ElggUser $user, $persistent = false) {
$code = (md5($user->name . $user->username . time() . rand()));
$_SESSION['code'] = $code;
$user->code = md5($code);
- setcookie("elggperm", $code, (time()+(86400 * 30)),"/");
+ setcookie("elggperm", $code, (time() + (86400 * 30)), "/");
}
- if (!$user->save() || !trigger_elgg_event('login','user',$user)) {
+ if (!$user->save() || !elgg_trigger_event('login', 'user', $user)) {
unset($_SESSION['username']);
unset($_SESSION['name']);
unset($_SESSION['code']);
unset($_SESSION['guid']);
unset($_SESSION['id']);
unset($_SESSION['user']);
- setcookie("elggperm", "", (time()-(86400 * 30)),"/");
- return false;
+ setcookie("elggperm", "", (time() - (86400 * 30)), "/");
+ throw new LoginException(elgg_echo('LoginException:Unknown'));
}
// Users privilege has been elevated, so change the session id (prevents session fixation)
@@ -417,26 +326,23 @@ function login(ElggUser $user, $persistent = false) {
set_last_login($_SESSION['guid']);
reset_login_failure_count($user->guid); // Reset any previous failed login attempts
- // Set admin shortcut flag if this is an admin
-// if (isadminloggedin()) {
-// //@todo REMOVE THIS.
-// global $is_admin;
-// $is_admin = true;
-// }
-
+ // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143
+ if (is_memcache_available()) {
+ // this needs to happen with a shutdown function because of the timing with set_last_login()
+ register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']);
+ }
+
return true;
}
/**
* Log the current user out
*
- * @return true|false
+ * @return bool
*/
function logout() {
- global $CONFIG;
-
if (isset($_SESSION['user'])) {
- if (!trigger_elgg_event('logout','user',$_SESSION['user'])) {
+ if (!elgg_trigger_event('logout', 'user', $_SESSION['user'])) {
return false;
}
$_SESSION['user']->code = "";
@@ -450,7 +356,7 @@ function logout() {
unset($_SESSION['id']);
unset($_SESSION['user']);
- setcookie("elggperm", "", (time()-(86400 * 30)),"/");
+ setcookie("elggperm", "", (time() - (86400 * 30)), "/");
// pass along any messages
$old_msg = $_SESSION['msg'];
@@ -458,71 +364,47 @@ function logout() {
session_destroy();
// starting a default session to store any post-logout messages.
- session_init(NULL, NULL, NULL);
+ _elgg_session_boot(NULL, NULL, NULL);
$_SESSION['msg'] = $old_msg;
return TRUE;
}
/**
- * Returns a fingerprint for an elgg session.
- *
- * @return string
- */
-function get_session_fingerprint() {
- global $CONFIG;
-
- return md5($_SERVER['HTTP_USER_AGENT'] . get_site_secret());
-}
-
-/**
* Initialises the system session and potentially logs the user in
*
* This function looks for:
*
* 1. $_SESSION['id'] - if not present, we're logged out, and this is set to 0
- * 2. The cookie 'elggperm' - if present, checks it for an authentication token, validates it, and potentially logs the user in
+ * 2. The cookie 'elggperm' - if present, checks it for an authentication
+ * token, validates it, and potentially logs the user in
*
* @uses $_SESSION
- * @param unknown_type $event
- * @param unknown_type $object_type
- * @param unknown_type $object
+ *
+ * @return bool
+ * @access private
*/
-function session_init($event, $object_type, $object) {
+function _elgg_session_boot() {
global $DB_PREFIX, $CONFIG;
- if (!is_db_installed()) {
- return false;
- }
-
// Use database for sessions
// HACK to allow access to prefix after object destruction
$DB_PREFIX = $CONFIG->dbprefix;
if ((!isset($CONFIG->use_file_sessions))) {
- session_set_save_handler("__elgg_session_open",
- "__elgg_session_close",
- "__elgg_session_read",
- "__elgg_session_write",
- "__elgg_session_destroy",
- "__elgg_session_gc");
+ session_set_save_handler("_elgg_session_open",
+ "_elgg_session_close",
+ "_elgg_session_read",
+ "_elgg_session_write",
+ "_elgg_session_destroy",
+ "_elgg_session_gc");
}
session_name('Elgg');
session_start();
- // Do some sanity checking by generating a fingerprint (makes some XSS attacks harder)
- if (isset($_SESSION['__elgg_fingerprint'])) {
- if ($_SESSION['__elgg_fingerprint'] != get_session_fingerprint()) {
- session_destroy();
- return false;
- }
- } else {
- $_SESSION['__elgg_fingerprint'] = get_session_fingerprint();
- }
-
// Generate a simple token (private from potentially public session id)
if (!isset($_SESSION['__elgg_session'])) {
- $_SESSION['__elgg_session'] = md5(microtime().rand());
+ $_SESSION['__elgg_session'] = md5(microtime() . rand());
}
// test whether we have a user session
@@ -567,8 +449,8 @@ function session_init($event, $object_type, $object) {
set_last_action($_SESSION['guid']);
}
- register_action("login",true);
- register_action("logout");
+ elgg_register_action('login', '', 'public');
+ elgg_register_action('logout');
// Register a default PAM handler
register_pam_handler('pam_auth_userpass');
@@ -583,42 +465,48 @@ function session_init($event, $object_type, $object) {
return false;
}
- // Since we have loaded a new user, this user may have different language preferences
- register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
-
return true;
}
/**
* Used at the top of a page to mark it as logged in users only.
*
+ * @return void
*/
function gatekeeper() {
- if (!isloggedin()) {
+ if (!elgg_is_logged_in()) {
$_SESSION['last_forward_from'] = current_page_url();
register_error(elgg_echo('loggedinrequired'));
- forward();
+ forward('', 'login');
}
}
/**
* Used at the top of a page to mark it as logged in admin or siteadmin only.
*
+ * @return void
*/
function admin_gatekeeper() {
gatekeeper();
- if (!isadminloggedin()) {
+ if (!elgg_is_admin_logged_in()) {
$_SESSION['last_forward_from'] = current_page_url();
register_error(elgg_echo('adminrequired'));
- forward();
+ forward('', 'admin');
}
}
/**
- * DB Based session handling code.
+ * Handles opening a session in the DB
+ *
+ * @param string $save_path The path to save the sessions
+ * @param string $session_name The name of the session
+ *
+ * @return true
+ * @todo Document
+ * @access private
*/
-function __elgg_session_open($save_path, $session_name) {
+function _elgg_session_open($save_path, $session_name) {
global $sess_save_path;
$sess_save_path = $save_path;
@@ -626,16 +514,27 @@ function __elgg_session_open($save_path, $session_name) {
}
/**
- * DB Based session handling code.
+ * Closes a session
+ *
+ * @todo implement
+ * @todo document
+ *
+ * @return true
+ * @access private
*/
-function __elgg_session_close() {
+function _elgg_session_close() {
return true;
}
/**
- * DB Based session handling code.
+ * Read the session data from DB failing back to file.
+ *
+ * @param string $id The session ID
+ *
+ * @return string
+ * @access private
*/
-function __elgg_session_read($id) {
+function _elgg_session_read($id) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -661,9 +560,15 @@ function __elgg_session_read($id) {
}
/**
- * DB Based session handling code.
+ * Write session data to the DB falling back to file.
+ *
+ * @param string $id The session ID
+ * @param mixed $sess_data Session data
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_write($id, $sess_data) {
+function _elgg_session_write($id, $sess_data) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -676,7 +581,7 @@ function __elgg_session_write($id, $sess_data) {
(session, ts, data) VALUES
('$id', '$time', '$sess_data_sanitised')";
- if (insert_data($q)!==false) {
+ if (insert_data($q) !== false) {
return true;
}
} catch (DatabaseException $e) {
@@ -696,9 +601,14 @@ function __elgg_session_write($id, $sess_data) {
}
/**
- * DB Based session handling code.
+ * Destroy a DB session, falling back to file.
+ *
+ * @param string $id Session ID
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_destroy($id) {
+function _elgg_session_destroy($id) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -711,24 +621,28 @@ function __elgg_session_destroy($id) {
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
- return(@unlink($sess_file));
+ return @unlink($sess_file);
}
-
- return false;
}
/**
- * DB Based session handling code.
+ * Perform garbage collection on session table / files
+ *
+ * @param int $maxlifetime Max age of a session
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_gc($maxlifetime) {
+function _elgg_session_gc($maxlifetime) {
global $DB_PREFIX;
- $life = time()-$maxlifetime;
+ $life = time() - $maxlifetime;
try {
return (bool)delete_data("DELETE from {$DB_PREFIX}users_sessions where ts<'$life'");
} catch (DatabaseException $e) {
- // Fall back to file store in this case, since this likely means that the database hasn't been upgraded
+ // Fall back to file store in this case, since this likely means that the database
+ // hasn't been upgraded
global $sess_save_path;
foreach (glob("$sess_save_path/sess_*") as $filename) {
@@ -740,5 +654,3 @@ function __elgg_session_gc($maxlifetime) {
return true;
}
-
-register_elgg_event_handler("boot","system","session_init",20);