Merge pull request #7907 from nupplaphil/task/reduce_app_deps
Cleanup Session/Authentication
This commit is contained in:
commit
6e4a428c73
32 changed files with 8791 additions and 7936 deletions
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file /src/Core/Authentication.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Network\HTTPException\ForbiddenException;
|
||||
|
||||
/**
|
||||
* Handle Authentification, Session and Cookies
|
||||
*/
|
||||
class Authentication extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @brief Calculate the hash that is needed for the "Friendica" cookie
|
||||
*
|
||||
* @param array $user Record from "user" table
|
||||
*
|
||||
* @return string Hashed data
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getCookieHashForUser($user)
|
||||
{
|
||||
return hash_hmac(
|
||||
"sha256",
|
||||
hash_hmac("sha256", $user["password"], $user["prvkey"]),
|
||||
Config::get("system", "site_prvkey")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the "Friendica" cookie
|
||||
*
|
||||
* @param int $time
|
||||
* @param array $user Record from "user" table
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function setCookie($time, $user = [])
|
||||
{
|
||||
if ($time != 0) {
|
||||
$time = $time + time();
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
$value = json_encode([
|
||||
"uid" => $user["uid"],
|
||||
"hash" => self::getCookieHashForUser($user),
|
||||
"ip" => ($_SERVER['REMOTE_ADDR'] ?? '') ?: '0.0.0.0'
|
||||
]);
|
||||
} else {
|
||||
$value = "";
|
||||
}
|
||||
|
||||
setcookie("Friendica", $value, $time, "/", "", (Config::get('system', 'ssl_policy') == App\BaseURL::SSL_POLICY_FULL), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Kills the "Friendica" cookie and all session data
|
||||
*/
|
||||
public static function deleteSession()
|
||||
{
|
||||
self::setCookie(-3600); // make sure cookie is deleted on browser close, as a security measure
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
public static function twoFactorCheck($uid, App $a)
|
||||
{
|
||||
// Check user setting, if 2FA disabled return
|
||||
if (!PConfig::get($uid, '2fa', 'verified')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check current path, if 2fa authentication module return
|
||||
if ($a->argc > 0 && in_array($a->argv[0], ['2fa', 'view', 'help', 'api', 'proxy', 'logout'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 1: 2FA session present and valid: return
|
||||
if (Session::get('2fa')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 2: No valid 2FA session: redirect to code verification page
|
||||
if ($a->isAjax()) {
|
||||
throw new ForbiddenException();
|
||||
} else {
|
||||
$a->internalRedirect('2fa');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ namespace Friendica\Core\L10n;
|
|||
|
||||
use Friendica\Core\Config\Configuration;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Core\Session\ISession;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -53,12 +53,14 @@ class L10n
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
public function __construct(Configuration $config, Database $dba, LoggerInterface $logger, array $server, array $get)
|
||||
public function __construct(Configuration $config, Database $dba, LoggerInterface $logger, ISession $session, array $server, array $get)
|
||||
{
|
||||
$this->dba = $dba;
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->loadTranslationTable(L10n::detectLanguage($server, $get, $config->get('system', 'language', 'en')));
|
||||
$this->setSessionVariable($session);
|
||||
$this->setLangFromSession($session);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,28 +76,28 @@ class L10n
|
|||
/**
|
||||
* Sets the language session variable
|
||||
*/
|
||||
public function setSessionVariable()
|
||||
private function setSessionVariable(ISession $session)
|
||||
{
|
||||
if (Session::get('authenticated') && !Session::get('language')) {
|
||||
$_SESSION['language'] = $this->lang;
|
||||
if ($session->get('authenticated') && !$session->get('language')) {
|
||||
$session->set('language', $this->lang);
|
||||
// we haven't loaded user data yet, but we need user language
|
||||
if (Session::get('uid')) {
|
||||
if ($session->get('uid')) {
|
||||
$user = $this->dba->selectFirst('user', ['language'], ['uid' => $_SESSION['uid']]);
|
||||
if ($this->dba->isResult($user)) {
|
||||
$_SESSION['language'] = $user['language'];
|
||||
$session->set('language', $user['language']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['lang'])) {
|
||||
Session::set('language', $_GET['lang']);
|
||||
$session->set('language', $_GET['lang']);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLangFromSession()
|
||||
private function setLangFromSession(ISession $session)
|
||||
{
|
||||
if (Session::get('language') !== $this->lang) {
|
||||
$this->loadTranslationTable(Session::get('language'));
|
||||
if ($session->get('language') !== $this->lang) {
|
||||
$this->loadTranslationTable($session->get('language'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
*/
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Session\CacheSessionHandler;
|
||||
use Friendica\Core\Session\DatabaseSessionHandler;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Session\ISession;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
@ -19,200 +16,39 @@ use Friendica\Util\Strings;
|
|||
*
|
||||
* @author Hypolite Petovan <hypolite@mrpetovan.com>
|
||||
*/
|
||||
class Session
|
||||
class Session extends BaseObject
|
||||
{
|
||||
public static $exists = false;
|
||||
public static $expire = 180000;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
ini_set('session.gc_probability', 50);
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
|
||||
if (Config::get('system', 'ssl_policy') == App\BaseURL::SSL_POLICY_FULL) {
|
||||
ini_set('session.cookie_secure', 1);
|
||||
}
|
||||
|
||||
$session_handler = Config::get('system', 'session_handler', 'database');
|
||||
if ($session_handler != 'native') {
|
||||
if ($session_handler == 'cache' && Config::get('system', 'cache_driver', 'database') != 'database') {
|
||||
$SessionHandler = new CacheSessionHandler();
|
||||
} else {
|
||||
$SessionHandler = new DatabaseSessionHandler();
|
||||
}
|
||||
|
||||
session_set_save_handler($SessionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static function exists($name)
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
return self::getClass(ISession::class)->exists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a key from the session super global or the defaults if the key is missing or the value is falsy.
|
||||
*
|
||||
* Handle the case where session_start() hasn't been called and the super global isn't available.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $defaults
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name, $defaults = null)
|
||||
{
|
||||
return $_SESSION[$name] ?? $defaults;
|
||||
return self::getClass(ISession::class)->get($name, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a single session variable.
|
||||
* Overrides value of existing key.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function set($name, $value)
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
self::getClass(ISession::class)->set($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple session variables.
|
||||
* Overrides values for existing keys.
|
||||
*
|
||||
* @param array $values
|
||||
*/
|
||||
public static function setMultiple(array $values)
|
||||
{
|
||||
$_SESSION = $values + $_SESSION;
|
||||
self::getClass(ISession::class)->setMultiple($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a session variable.
|
||||
* Ignores missing keys.
|
||||
*
|
||||
* @param $name
|
||||
*/
|
||||
public static function remove($name)
|
||||
{
|
||||
unset($_SESSION[$name]);
|
||||
self::getClass(ISession::class)->remove($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current session array
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the provided user's authenticated session
|
||||
*
|
||||
* @param App $a
|
||||
* @param array $user_record
|
||||
* @param bool $login_initial
|
||||
* @param bool $interactive
|
||||
* @param bool $login_refresh
|
||||
* @throws \Friendica\Network\HTTPException\ForbiddenException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function setAuthenticatedForUser(App $a, array $user_record, $login_initial = false, $interactive = false, $login_refresh = false)
|
||||
{
|
||||
self::setMultiple([
|
||||
'uid' => $user_record['uid'],
|
||||
'theme' => $user_record['theme'],
|
||||
'mobile-theme' => PConfig::get($user_record['uid'], 'system', 'mobile_theme'),
|
||||
'authenticated' => 1,
|
||||
'page_flags' => $user_record['page-flags'],
|
||||
'my_url' => $a->getBaseURL() . '/profile/' . $user_record['nickname'],
|
||||
'my_address' => $user_record['nickname'] . '@' . substr($a->getBaseURL(), strpos($a->getBaseURL(), '://') + 3),
|
||||
'addr' => ($_SERVER['REMOTE_ADDR'] ?? '') ?: '0.0.0.0'
|
||||
]);
|
||||
|
||||
self::setVisitorsContacts();
|
||||
|
||||
$member_since = strtotime($user_record['register_date']);
|
||||
self::set('new_member', time() < ($member_since + ( 60 * 60 * 24 * 14)));
|
||||
|
||||
if (strlen($user_record['timezone'])) {
|
||||
date_default_timezone_set($user_record['timezone']);
|
||||
$a->timezone = $user_record['timezone'];
|
||||
}
|
||||
|
||||
$masterUid = $user_record['uid'];
|
||||
|
||||
if (self::get('submanage')) {
|
||||
$user = DBA::selectFirst('user', ['uid'], ['uid' => self::get('submanage')]);
|
||||
if (DBA::isResult($user)) {
|
||||
$masterUid = $user['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
$a->identities = User::identities($masterUid);
|
||||
|
||||
if ($login_initial) {
|
||||
$a->getLogger()->info('auth_identities: ' . print_r($a->identities, true));
|
||||
}
|
||||
|
||||
if ($login_refresh) {
|
||||
$a->getLogger()->info('auth_identities refresh: ' . print_r($a->identities, true));
|
||||
}
|
||||
|
||||
$contact = DBA::selectFirst('contact', [], ['uid' => $user_record['uid'], 'self' => true]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$a->contact = $contact;
|
||||
$a->cid = $contact['id'];
|
||||
self::set('cid', $a->cid);
|
||||
}
|
||||
|
||||
header('X-Account-Management-Status: active; name="' . $user_record['username'] . '"; id="' . $user_record['nickname'] . '"');
|
||||
|
||||
if ($login_initial || $login_refresh) {
|
||||
DBA::update('user', ['login_date' => DateTimeFormat::utcNow()], ['uid' => $user_record['uid']]);
|
||||
|
||||
// Set the login date for all identities of the user
|
||||
DBA::update('user', ['login_date' => DateTimeFormat::utcNow()],
|
||||
['parent-uid' => $masterUid, 'account_removed' => false]);
|
||||
}
|
||||
|
||||
if ($login_initial) {
|
||||
/*
|
||||
* If the user specified to remember the authentication, then set a cookie
|
||||
* that expires after one week (the default is when the browser is closed).
|
||||
* The cookie will be renewed automatically.
|
||||
* The week ensures that sessions will expire after some inactivity.
|
||||
*/
|
||||
;
|
||||
if (self::get('remember')) {
|
||||
$a->getLogger()->info('Injecting cookie for remembered user ' . $user_record['nickname']);
|
||||
Authentication::setCookie(604800, $user_record);
|
||||
self::remove('remember');
|
||||
}
|
||||
}
|
||||
|
||||
Authentication::twoFactorCheck($user_record['uid'], $a);
|
||||
|
||||
if ($interactive) {
|
||||
if ($user_record['login_date'] <= DBA::NULL_DATETIME) {
|
||||
info(L10n::t('Welcome %s', $user_record['username']));
|
||||
info(L10n::t('Please upload a profile photo.'));
|
||||
$a->internalRedirect('profile_photo/new');
|
||||
} else {
|
||||
info(L10n::t("Welcome back %s", $user_record['username']));
|
||||
}
|
||||
}
|
||||
|
||||
$a->user = $user_record;
|
||||
|
||||
if ($login_initial) {
|
||||
Hook::callAll('logged_in', $a->user);
|
||||
|
||||
if ($a->module !== 'home' && self::exists('return_path')) {
|
||||
$a->internalRedirect(self::get('return_path'));
|
||||
}
|
||||
}
|
||||
self::getClass(ISession::class)->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,11 +59,14 @@ class Session
|
|||
*/
|
||||
public static function getRemoteContactID($uid)
|
||||
{
|
||||
if (empty($_SESSION['remote'][$uid])) {
|
||||
/** @var ISession $session */
|
||||
$session = self::getClass(ISession::class);
|
||||
|
||||
if (empty($session->get('remote')[$uid])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $_SESSION['remote'][$uid];
|
||||
return $session->get('remote')[$uid];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,11 +77,14 @@ class Session
|
|||
*/
|
||||
public static function getUserIDForVisitorContactID($cid)
|
||||
{
|
||||
if (empty($_SESSION['remote'])) {
|
||||
/** @var ISession $session */
|
||||
$session = self::getClass(ISession::class);
|
||||
|
||||
if (empty($session->get('remote'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_search($cid, $_SESSION['remote']);
|
||||
return array_search($cid, $session->get('remote'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,15 +94,18 @@ class Session
|
|||
*/
|
||||
public static function setVisitorsContacts()
|
||||
{
|
||||
$_SESSION['remote'] = [];
|
||||
/** @var ISession $session */
|
||||
$session = self::getClass(ISession::class);
|
||||
|
||||
$remote_contacts = DBA::select('contact', ['id', 'uid'], ['nurl' => Strings::normaliseLink($_SESSION['my_url']), 'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'self' => false]);
|
||||
$session->set('remote', []);
|
||||
|
||||
$remote_contacts = DBA::select('contact', ['id', 'uid'], ['nurl' => Strings::normaliseLink($session->get('my_url')), 'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'self' => false]);
|
||||
while ($contact = DBA::fetch($remote_contacts)) {
|
||||
if (($contact['uid'] == 0) || Contact::isBlockedByUser($contact['id'], $contact['uid'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$_SESSION['remote'][$contact['uid']] = $contact['id'];
|
||||
$session->set('remote', [$contact['uid'] => $contact['id']]);
|
||||
}
|
||||
DBA::close($remote_contacts);
|
||||
}
|
||||
|
@ -272,10 +117,9 @@ class Session
|
|||
*/
|
||||
public static function isAuthenticated()
|
||||
{
|
||||
if (empty($_SESSION['authenticated'])) {
|
||||
return false;
|
||||
}
|
||||
/** @var ISession $session */
|
||||
$session = self::getClass(ISession::class);
|
||||
|
||||
return $_SESSION['authenticated'];
|
||||
return $session->get('authenticated', false);
|
||||
}
|
||||
}
|
||||
|
|
76
src/Core/Session/AbstractSession.php
Normal file
76
src/Core/Session/AbstractSession.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
|
||||
use Friendica\Model\User\Cookie;
|
||||
|
||||
/**
|
||||
* Contains the base methods for $_SESSION interaction
|
||||
*/
|
||||
class AbstractSession
|
||||
{
|
||||
/** @var Cookie */
|
||||
protected $cookie;
|
||||
|
||||
public function __construct( Cookie $cookie)
|
||||
{
|
||||
$this->cookie = $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}}
|
||||
*/
|
||||
public function exists(string $name)
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get(string $name, $defaults = null)
|
||||
{
|
||||
return $_SESSION[$name] ?? $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function set(string $name, $value)
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setMultiple(array $values)
|
||||
{
|
||||
$_SESSION = $values + $_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function remove(string $name)
|
||||
{
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
* SessionHandler using database
|
||||
*
|
||||
* @author Hypolite Petovan <hypolite@mrpetovan.com>
|
||||
*/
|
||||
class DatabaseSessionHandler extends BaseObject implements SessionHandlerInterface
|
||||
{
|
||||
public function open($save_path, $session_name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function read($session_id)
|
||||
{
|
||||
if (empty($session_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$session = DBA::selectFirst('session', ['data'], ['sid' => $session_id]);
|
||||
if (DBA::isResult($session)) {
|
||||
Session::$exists = true;
|
||||
return $session['data'];
|
||||
}
|
||||
|
||||
Logger::notice('no data for session', ['session_id' => $session_id, 'uri' => $_SERVER['REQUEST_URI']]);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Standard PHP session write callback
|
||||
*
|
||||
* This callback updates the DB-stored session data and/or the expiration depending
|
||||
* on the case. Uses the Session::expire global for existing session, 5 minutes
|
||||
* for newly created session.
|
||||
*
|
||||
* @param string $session_id Session ID with format: [a-z0-9]{26}
|
||||
* @param string $session_data Serialized session data
|
||||
* @return boolean Returns false if parameters are missing, true otherwise
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
if (!$session_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$session_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$expire = time() + Session::$expire;
|
||||
$default_expire = time() + 300;
|
||||
|
||||
if (Session::$exists) {
|
||||
$fields = ['data' => $session_data, 'expire' => $expire];
|
||||
$condition = ["`sid` = ? AND (`data` != ? OR `expire` != ?)", $session_id, $session_data, $expire];
|
||||
DBA::update('session', $fields, $condition);
|
||||
} else {
|
||||
$fields = ['sid' => $session_id, 'expire' => $default_expire, 'data' => $session_data];
|
||||
DBA::insert('session', $fields);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
DBA::delete('session', ['sid' => $id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
DBA::delete('session', ["`expire` < ?", time()]);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
namespace Friendica\Core\Session\Handler;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Cache;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Cache\ICache;
|
||||
use Friendica\Core\Session;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
|
@ -13,8 +12,22 @@ use SessionHandlerInterface;
|
|||
*
|
||||
* @author Hypolite Petovan <hypolite@mrpetovan.com>
|
||||
*/
|
||||
class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
|
||||
final class Cache implements SessionHandlerInterface
|
||||
{
|
||||
/** @var ICache */
|
||||
private $cache;
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
/** @var array The $_SERVER array */
|
||||
private $server;
|
||||
|
||||
public function __construct(ICache $cache, LoggerInterface $logger, array $server)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
public function open($save_path, $session_name)
|
||||
{
|
||||
return true;
|
||||
|
@ -26,13 +39,13 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
|
|||
return '';
|
||||
}
|
||||
|
||||
$data = Cache::get('session:' . $session_id);
|
||||
$data = $this->cache->get('session:' . $session_id);
|
||||
if (!empty($data)) {
|
||||
Session::$exists = true;
|
||||
return $data;
|
||||
}
|
||||
|
||||
Logger::notice('no data for session', ['session_id' => $session_id, 'uri' => $_SERVER['REQUEST_URI']]);
|
||||
$this->logger->notice('no data for session', ['session_id' => $session_id, 'uri' => $this->server['REQUEST_URI'] ?? '']);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
@ -44,8 +57,9 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
|
|||
* on the case. Uses the Session::expire for existing session, 5 minutes
|
||||
* for newly created session.
|
||||
*
|
||||
* @param string $session_id Session ID with format: [a-z0-9]{26}
|
||||
* @param string $session_data Serialized session data
|
||||
* @param string $session_id Session ID with format: [a-z0-9]{26}
|
||||
* @param string $session_data Serialized session data
|
||||
*
|
||||
* @return boolean Returns false if parameters are missing, true otherwise
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
@ -59,9 +73,7 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
|
|||
return true;
|
||||
}
|
||||
|
||||
$return = Cache::set('session:' . $session_id, $session_data, Session::$expire);
|
||||
|
||||
return $return;
|
||||
return $this->cache->set('session:' . $session_id, $session_data, Session::$expire);
|
||||
}
|
||||
|
||||
public function close()
|
||||
|
@ -71,9 +83,7 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
|
|||
|
||||
public function destroy($id)
|
||||
{
|
||||
$return = Cache::delete('session:' . $id);
|
||||
|
||||
return $return;
|
||||
return $this->cache->delete('session:' . $id);
|
||||
}
|
||||
|
||||
public function gc($maxlifetime)
|
112
src/Core/Session/Handler/Database.php
Normal file
112
src/Core/Session/Handler/Database.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session\Handler;
|
||||
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\Database as DBA;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
* SessionHandler using database
|
||||
*
|
||||
* @author Hypolite Petovan <hypolite@mrpetovan.com>
|
||||
*/
|
||||
final class Database implements SessionHandlerInterface
|
||||
{
|
||||
/** @var DBA */
|
||||
private $dba;
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
/** @var array The $_SERVER variable */
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* DatabaseSessionHandler constructor.
|
||||
*
|
||||
* @param DBA $dba
|
||||
* @param LoggerInterface $logger
|
||||
* @param array $server
|
||||
*/
|
||||
public function __construct(DBA $dba, LoggerInterface $logger, array $server)
|
||||
{
|
||||
$this->dba = $dba;
|
||||
$this->logger = $logger;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
public function open($save_path, $session_name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function read($session_id)
|
||||
{
|
||||
if (empty($session_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$session = $this->dba->selectFirst('session', ['data'], ['sid' => $session_id]);
|
||||
if ($this->dba->isResult($session)) {
|
||||
Session::$exists = true;
|
||||
return $session['data'];
|
||||
}
|
||||
|
||||
$this->logger->notice('no data for session', ['session_id' => $session_id, 'uri' => $this->server['REQUEST_URI'] ?? '']);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Standard PHP session write callback
|
||||
*
|
||||
* This callback updates the DB-stored session data and/or the expiration depending
|
||||
* on the case. Uses the Session::expire global for existing session, 5 minutes
|
||||
* for newly created session.
|
||||
*
|
||||
* @param string $session_id Session ID with format: [a-z0-9]{26}
|
||||
* @param string $session_data Serialized session data
|
||||
*
|
||||
* @return boolean Returns false if parameters are missing, true otherwise
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
if (!$session_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$session_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$expire = time() + Session::$expire;
|
||||
$default_expire = time() + 300;
|
||||
|
||||
if (Session::$exists) {
|
||||
$fields = ['data' => $session_data, 'expire' => $expire];
|
||||
$condition = ["`sid` = ? AND (`data` != ? OR `expire` != ?)", $session_id, $session_data, $expire];
|
||||
$this->dba->update('session', $fields, $condition);
|
||||
} else {
|
||||
$fields = ['sid' => $session_id, 'expire' => $default_expire, 'data' => $session_data];
|
||||
$this->dba->insert('session', $fields);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
return $this->dba->delete('session', ['sid' => $id]);
|
||||
}
|
||||
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->dba->delete('session', ["`expire` < ?", time()]);
|
||||
}
|
||||
}
|
67
src/Core/Session/ISession.php
Normal file
67
src/Core/Session/ISession.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
|
||||
/**
|
||||
* Contains all global supported Session methods
|
||||
*/
|
||||
interface ISession
|
||||
{
|
||||
/**
|
||||
* Start the current session
|
||||
*
|
||||
* @return self The own Session instance
|
||||
*/
|
||||
public function start();
|
||||
|
||||
/**
|
||||
* Checks if the key exists in this session
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean True, if it exists
|
||||
*/
|
||||
public function exists(string $name);
|
||||
|
||||
/**
|
||||
* Retrieves a key from the session super global or the defaults if the key is missing or the value is falsy.
|
||||
*
|
||||
* Handle the case where session_start() hasn't been called and the super global isn't available.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $defaults
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $name, $defaults = null);
|
||||
|
||||
/**
|
||||
* Sets a single session variable.
|
||||
* Overrides value of existing key.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set(string $name, $value);
|
||||
|
||||
/**
|
||||
* Sets multiple session variables.
|
||||
* Overrides values for existing keys.
|
||||
*
|
||||
* @param array $values
|
||||
*/
|
||||
public function setMultiple(array $values);
|
||||
|
||||
/**
|
||||
* Removes a session variable.
|
||||
* Ignores missing keys.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function remove(string $name);
|
||||
|
||||
/**
|
||||
* Clears the current session array
|
||||
*/
|
||||
public function clear();
|
||||
}
|
22
src/Core/Session/Memory.php
Normal file
22
src/Core/Session/Memory.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
|
||||
use Friendica\Model\User\Cookie;
|
||||
|
||||
/**
|
||||
* Usable for backend processes (daemon/worker) and testing
|
||||
*
|
||||
* @todo after replacing the last direct $_SESSION call, use a internal array instead of the global variable
|
||||
*/
|
||||
final class Memory extends AbstractSession implements ISession
|
||||
{
|
||||
public function __construct(Cookie $cookie)
|
||||
{
|
||||
parent::__construct($cookie);
|
||||
|
||||
// Backward compatibility until all Session variables are replaced
|
||||
// with the Session class
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
39
src/Core/Session/Native.php
Normal file
39
src/Core/Session/Native.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Session;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Model\User\Cookie;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
* The native Session class which uses the PHP internal Session functions
|
||||
*/
|
||||
final class Native extends AbstractSession implements ISession
|
||||
{
|
||||
public function __construct(App\BaseURL $baseURL, Cookie $cookie, SessionHandlerInterface $handler = null)
|
||||
{
|
||||
parent::__construct($cookie);
|
||||
|
||||
ini_set('session.gc_probability', 50);
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
ini_set('session.cookie_httponly', (int)Cookie::HTTPONLY);
|
||||
|
||||
if ($baseURL->getSSLPolicy() == App\BaseURL::SSL_POLICY_FULL) {
|
||||
ini_set('session.cookie_secure', 1);
|
||||
}
|
||||
|
||||
if (isset($handler)) {
|
||||
session_set_save_handler($handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
session_start();
|
||||
return $this;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue