engine/classes/ElggSession.php
Rama 7.x · réplica de lectura del repositorio original.
<?php
use Elgg\Config;
use Elgg\Database;
use Elgg\Database\SessionHandler as ElggSessionHandler;
use Elgg\Exceptions\SecurityException;
use Elgg\Traits\Debug\Profilable;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
/**
* Elgg Session Management
*
* Reserved keys: last_forward_from, msg, sticky_forms, user, guid, id, code, name, username
*
* @see elgg_get_session()
*/
class ElggSession {
use Profilable;
/**
* @var SessionInterface
*/
protected $storage;
/**
* Constructor
*
* @param SessionInterface $storage The underlying Session implementation
*/
public function __construct(SessionInterface $storage) {
$this->storage = $storage;
}
/**
* Initializes the session and checks for the remember me cookie
*
* @return void
* @internal
*/
public function boot(): void {
$this->beginTimer([__METHOD__]);
$this->start();
$migrate_session = false;
// test whether we have a user session
if ($this->has('guid')) {
$user = _elgg_services()->entityTable->get($this->get('guid'), 'user');
if (!$user instanceof \ElggUser) {
// OMG user has been deleted.
$this->invalidate();
// redirect to homepage
$this->endTimer([__METHOD__]);
_elgg_services()->responseFactory->redirect('');
}
} else {
$user = _elgg_services()->persistentLogin->bootSession();
if ($user instanceof \ElggUser) {
_elgg_services()->persistentLogin->updateTokenUsage($user);
_elgg_services()->session_manager->setUserToken($user);
$migrate_session = true;
}
}
if ($user instanceof \ElggUser) {
_elgg_services()->session_manager->setLoggedInUser($user, $migrate_session);
$user->setLastAction();
// logout a user with open session who has been banned
if ($user->isBanned()) {
_elgg_services()->session_manager->logout();
}
// make sure the user hasn't changed in between requests
// e.g. password reset
try {
_elgg_services()->session_manager->validateUserToken($user);
} catch (SecurityException $e) {
elgg_register_error_message($e->getMessage());
_elgg_services()->session_manager->logout();
}
}
$this->endTimer([__METHOD__]);
}
/**
* Start the session
*
* @return boolean
* @throws RuntimeException If session fails to start.
* @since 1.9
*/
public function start() {
if ($this->storage->getId()) {
return true;
}
$result = $this->storage->start();
$this->generateSessionToken();
return $result;
}
/**
* Migrates the session to a new session id while maintaining session attributes
*
* @param boolean $destroy Whether to delete the session or let gc handle clean up
* @return boolean
* @since 1.9
*/
public function migrate(bool $destroy = true) {
return $this->storage->migrate($destroy);
}
/**
* Invalidates the session
*
* Deletes session data and session persistence. Starts a new session.
*
* @return boolean
* @since 1.9
*/
public function invalidate() {
$this->storage->clear();
$result = $this->migrate(true);
$this->generateSessionToken();
_elgg_services()->accessCache->clear();
return $result;
}
/**
* Save the session data and closes the session
*
* @return void
* @since 3.0
*/
public function save() {
$this->storage->save();
}
/**
* Has the session been started
*
* @return boolean
* @since 1.9
*/
public function isStarted() {
return $this->storage->isStarted();
}
/**
* Get the session ID
*
* @return string
* @since 1.9
*/
public function getID() {
return $this->storage->getId();
}
/**
* Set the session ID
*
* @param string $id Session ID
* @return void
* @since 1.9
*/
public function setId($id) {
$this->storage->setId($id);
}
/**
* Get the session name
*
* @return string
* @since 1.9
*/
public function getName() {
return $this->storage->getName();
}
/**
* Set the session name
*
* @param string $name Session name
* @return void
* @since 1.9
*/
public function setName($name) {
$this->storage->setName($name);
}
/**
* Get an attribute of the session
*
* @param string $name Name of the attribute to get
* @param mixed $default Value to return if attribute is not set (default is null)
* @return mixed
*/
public function get($name, $default = null) {
return $this->storage->get($name, $default);
}
/**
* Set an attribute
*
* @param string $name Name of the attribute to set
* @param mixed $value Value to be set
* @return void
*/
public function set($name, $value) {
$this->storage->set($name, $value);
}
/**
* Remove an attribute
*
* @param string $name The name of the attribute to remove
* @return mixed The removed attribute
* @since 1.9
*/
public function remove($name) {
return $this->storage->remove($name);
}
/**
* Has the attribute been defined
*
* @param string $name Name of the attribute
* @return bool
* @since 1.9
*/
public function has($name) {
return $this->storage->has($name);
}
/**
* Adds a token to the session
*
* This is used in creation of CSRF token, and is passed to the client to allow validating tokens
* later, even if the PHP session was destroyed.
*
* @return void
*/
protected function generateSessionToken() {
// Generate a simple token that we store server side
if (!$this->has('__elgg_session')) {
$this->set('__elgg_session', _elgg_services()->crypto->getRandomString(22));
}
}
/**
* Get an isolated ElggSession that does not persist between requests
*
* @return self
*
* @internal
*/
public static function getMock() {
$storage = new MockArraySessionStorage();
$session = new Session($storage);
return new self($session);
}
/**
* Create a session stored in the DB.
*
* @param Config $config Config
* @param Database $db Database
*
* @return ElggSession
*
* @internal
*/
public static function fromDatabase(Config $config, Database $db) {
$params = $config->getCookieConfig()['session'];
$options = [
// session.cache_limiter is unfortunately set to "" by the NativeSessionStorage
// constructor, so we must capture and inject it directly.
'cache_limiter' => session_cache_limiter(),
'name' => $params['name'],
'cookie_path' => $params['path'],
'cookie_domain' => $params['domain'],
'cookie_secure' => $params['secure'],
'cookie_httponly' => $params['httponly'],
'cookie_lifetime' => $params['lifetime'],
'cookie_samesite' => $params['samesite'],
];
$handler = new ElggSessionHandler($db);
$storage = new NativeSessionStorage($options, $handler);
$session = new Session($storage);
return new self($session);
}
/**
* Create a session stored in files
*
* @param Config $config Config
*
* @return ElggSession
*
* @internal
*/
public static function fromFiles(Config $config) {
$params = $config->getCookieConfig()['session'];
$options = [
// session.cache_limiter is unfortunately set to "" by the NativeSessionStorage
// constructor, so we must capture and inject it directly.
'cache_limiter' => session_cache_limiter(),
'name' => $params['name'],
'cookie_path' => $params['path'],
'cookie_domain' => $params['domain'],
'cookie_secure' => $params['secure'],
'cookie_httponly' => $params['httponly'],
'cookie_lifetime' => $params['lifetime'],
];
$storage = new NativeSessionStorage($options);
$session = new Session($storage);
return new self($session);
}
}