Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -26,12 +26,7 @@ class _Admin extends \IPS\Session
|
||||
* @brief Unix Timestamp of log in time
|
||||
*/
|
||||
public $logInTime;
|
||||
|
||||
/**
|
||||
* @brief Key for cookie
|
||||
*/
|
||||
public $cookieKey;
|
||||
|
||||
|
||||
/**
|
||||
* Open Session
|
||||
*
|
||||
@@ -58,9 +53,8 @@ class _Admin extends \IPS\Session
|
||||
try
|
||||
{
|
||||
/* Load session */
|
||||
$session = \IPS\Db::i()->select( '*', 'core_sys_cp_sessions', array( 'session_id=?', $sessionId ) )->first();
|
||||
$session = \IPS\Db::i()->select( '*', 'core_sys_cp_sessions', array( 'session_id=? AND session_running_time>=?', $sessionId, ( time() - \IPS\Session::sessionLifetime() ) ) )->first();
|
||||
$this->logInTime = $session['session_log_in_time'];
|
||||
$this->cookieKey = $session['session_cookie_key'];
|
||||
|
||||
/* Store this so plugins can access */
|
||||
$this->sessionData = $session;
|
||||
@@ -71,19 +65,9 @@ class _Admin extends \IPS\Session
|
||||
{
|
||||
throw new \DomainException('NO_ACPACCESS');
|
||||
}
|
||||
|
||||
/* Validate adsess */
|
||||
if ( \IPS\Request::i()->adsess !== $session['session_id'] )
|
||||
{
|
||||
throw new \DomainException('NO_ADSESS');
|
||||
}
|
||||
if ( \IPS\Request::i()->cookie['acp_login_key'] !== $this->cookieKey )
|
||||
{
|
||||
throw new \DomainException('NO_COOKIE');
|
||||
}
|
||||
|
||||
|
||||
/* Check IP address */
|
||||
if ( ( defined( '\IPS\BYPASS_ACP_IP_CHECK' ) and !\IPS\BYPASS_ACP_IP_CHECK ) and \IPS\Settings::i()->match_ipaddress and $session['session_ip_address'] !== \IPS\Request::i()->ipAddress() )
|
||||
if ( ( \defined( '\IPS\BYPASS_ACP_IP_CHECK' ) and !\IPS\BYPASS_ACP_IP_CHECK ) and \IPS\Settings::i()->match_ipaddress and $session['session_ip_address'] !== \IPS\Request::i()->ipAddress() )
|
||||
{
|
||||
throw new \DomainException('BAD_IP');
|
||||
}
|
||||
@@ -96,7 +80,6 @@ class _Admin extends \IPS\Session
|
||||
$this->member = new \IPS\Member;
|
||||
$this->logInTime = 0;
|
||||
$this->error = $e;
|
||||
$this->cookieKey = \IPS\Login::generateRandomString();
|
||||
return isset( $this->sessionData['session_app_data'] ) ? $this->sessionData['session_app_data'] : '';
|
||||
}
|
||||
}
|
||||
@@ -119,8 +102,7 @@ class _Admin extends \IPS\Session
|
||||
'session_log_in_time' => $this->logInTime,
|
||||
'session_running_time' => time(),
|
||||
'session_url' => \IPS\Request::i()->url(),
|
||||
'session_app_data' => $data,
|
||||
'session_cookie_key' => $this->cookieKey
|
||||
'session_app_data' => $data
|
||||
) );
|
||||
|
||||
return TRUE;
|
||||
|
||||
+149
-15
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -41,7 +41,20 @@ class _Front extends \IPS\Session
|
||||
*/
|
||||
public static function loggedIn()
|
||||
{
|
||||
return isset( \IPS\Request::i()->cookie['member_id'] ) and \IPS\Request::i()->cookie['member_id'];
|
||||
/* If we have a "member_id" cookie, we're probably logged in... */
|
||||
if ( isset( \IPS\Request::i()->cookie['member_id'] ) and \IPS\Request::i()->cookie['member_id'] )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* If the request sent an access token which has GraphQL acceess we'll need to check that */
|
||||
if ( isset( $_SERVER['HTTP_X_IPS_ACCESSTOKENMEMBER'] ) or isset( \IPS\Request::i()->access_token_member ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Still here: assume not logged in */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,6 +67,11 @@ class _Front extends \IPS\Session
|
||||
*/
|
||||
protected $save = TRUE;
|
||||
|
||||
/**
|
||||
* @brief No write guest session?
|
||||
*/
|
||||
protected $noWriteGuestSession = FALSE;
|
||||
|
||||
/**
|
||||
* Open Session
|
||||
*
|
||||
@@ -78,11 +96,17 @@ class _Front extends \IPS\Session
|
||||
|
||||
/* Get user agent info */
|
||||
$this->userAgent = \IPS\Http\Useragent::parse();
|
||||
|
||||
|
||||
if ( ! static::loggedIn() and isset( \IPS\Request::i()->cookie['guestTime'] ) and ( !isset( \IPS\Request::i()->cookie['noCache'] ) or !\IPS\Request::i()->cookie['noCache'] ) and time() < ( \IPS\Request::i()->cookie['guestTime'] + \IPS\CACHE_PAGE_TIMEOUT ) and \IPS\Request::i()->requestMethod() == 'GET' )
|
||||
{
|
||||
$this->sessionData = $this->setNoWriteGuestSession();
|
||||
return (string) $this->sessionData['data'];
|
||||
}
|
||||
|
||||
$session = \IPS\Session\Store::i()->loadSession( $this->sessionId );
|
||||
|
||||
/* Only use sessions with matching IP address */
|
||||
if( \IPS\Settings::i()->match_ipaddress and $session['ip_address'] != \IPS\Request::i()->ipAddress() )
|
||||
if( $session and \IPS\Settings::i()->match_ipaddress and $session['ip_address'] != \IPS\Request::i()->ipAddress() )
|
||||
{
|
||||
$session = NULL;
|
||||
}
|
||||
@@ -99,11 +123,15 @@ class _Front extends \IPS\Session
|
||||
/* Got one? */
|
||||
if ( $session )
|
||||
{
|
||||
/* If this is a guest and the "running time" on this is less than 30 seconds ago, or if a member and less than 15 seconds ago, we don't need a database write */
|
||||
if ( ( !$session['member_id'] and $session['running_time'] > ( time() - 30 ) ) or ( $session['member_id'] and $session['running_time'] > ( time() - 15 ) ) )
|
||||
/* If this is a guest and the "running time" on this is less than the guest page cache, or if a member and less than 15 seconds ago, we don't need a database write */
|
||||
if ( ( !$session['member_id'] and $session['running_time'] < ( time() - \IPS\CACHE_PAGE_TIMEOUT ) ) or ( $session['member_id'] and $session['running_time'] < ( time() - 15 ) ) )
|
||||
{
|
||||
$this->save = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->save = FALSE;
|
||||
}
|
||||
|
||||
/* Set member */
|
||||
try
|
||||
@@ -120,8 +148,59 @@ class _Front extends \IPS\Session
|
||||
{
|
||||
$this->member = new \IPS\Member;
|
||||
}
|
||||
|
||||
/* If we don't have a member, but the request *did* send an access token which has GraphQL acceess (i.e. unfettered access to act as the user), then use that */
|
||||
if ( !$this->member->member_id and ( isset( $_SERVER['HTTP_X_IPS_ACCESSTOKENMEMBER'] ) or isset( \IPS\Request::i()->access_token_member ) ) and $authorizationHeader = \IPS\Request::i()->authorizationHeader() and mb_substr( $authorizationHeader, 0, 7 ) === 'Bearer ' and ( !\IPS\OAUTH_REQUIRES_HTTPS or \IPS\Request::i()->isSecure() ) )
|
||||
{
|
||||
$expectedMember = \IPS\Member::load( isset( $_SERVER['HTTP_X_IPS_ACCESSTOKENMEMBER'] ) ? $_SERVER['HTTP_X_IPS_ACCESSTOKENMEMBER'] : \IPS\Request::i()->access_token_member );
|
||||
if ( $expectedMember->member_id )
|
||||
{
|
||||
/* Start by checking the access token is valid and for this member */
|
||||
try
|
||||
{
|
||||
$accessToken = \IPS\Api\OAuthClient::accessTokenDetails( mb_substr( $authorizationHeader, 7 ) );
|
||||
$client = \IPS\Api\OAuthClient::load( $accessToken['client_id'] );
|
||||
if ( $client->graphql and $accessToken['member_id'] === $expectedMember->member_id )
|
||||
{
|
||||
$success = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$success = FALSE;
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$success = FALSE;
|
||||
}
|
||||
|
||||
/* Because this is effectively a log in attempt, we need to make sure the account is not locked */
|
||||
try
|
||||
{
|
||||
\IPS\Login::checkIfAccountIsLocked( $expectedMember, $success );
|
||||
|
||||
/* If it isn't, we can either set that we are that member... */
|
||||
if ( $success )
|
||||
{
|
||||
$this->member = $expectedMember;
|
||||
}
|
||||
/* Or if the access token wasn't valid, log it as a fail so that it can't be bruteforced */
|
||||
else
|
||||
{
|
||||
$failedLogins = \is_array( $expectedMember->failed_logins ) ? $expectedMember->failed_logins : array();
|
||||
$failedLogins[ \IPS\Request::i()->ipAddress() ][] = time();
|
||||
$expectedMember->failed_logins = $failedLogins;
|
||||
$expectedMember->save();
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
// Account is locked. Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't have a member, check the cookies */
|
||||
/* If we still don't have a member, check the cookies */
|
||||
$device = NULL;
|
||||
if ( !$this->member->member_id and isset( \IPS\Request::i()->cookie['device_key'] ) and isset( \IPS\Request::i()->cookie['member_id'] ) and isset( \IPS\Request::i()->cookie['login_key'] ) )
|
||||
{
|
||||
@@ -148,7 +227,7 @@ class _Front extends \IPS\Session
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
/* ... so log it as a failed login */
|
||||
$failedLogins = is_array( $member->failed_logins ) ? $member->failed_logins : array();
|
||||
$failedLogins = \is_array( $member->failed_logins ) ? $member->failed_logins : array();
|
||||
$failedLogins[ \IPS\Request::i()->ipAddress() ][] = time();
|
||||
$member->failed_logins = $failedLogins;
|
||||
$member->save();
|
||||
@@ -169,7 +248,7 @@ class _Front extends \IPS\Session
|
||||
/* Work out the type */
|
||||
if ( $this->member->member_id )
|
||||
{
|
||||
if ( ( $session and $session['login_type'] === static::LOGIN_TYPE_ANONYMOUS ) or ( $device and $device->anonymous ) OR $this->member->group['g_hide_online_list'] )
|
||||
if ( $this->member->group['g_hide_online_list'] != 2 AND ( ( $session and $session['login_type'] === static::LOGIN_TYPE_ANONYMOUS ) or ( $device and $device->anonymous ) OR $this->member->group['g_hide_online_list'] == 1 ) )
|
||||
{
|
||||
$type = static::LOGIN_TYPE_ANONYMOUS;
|
||||
}
|
||||
@@ -184,6 +263,8 @@ class _Front extends \IPS\Session
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Request::i()->setCookie( 'guestTime', time() );
|
||||
|
||||
$type = $this->userAgent->spider ? static::LOGIN_TYPE_SPIDER : static::LOGIN_TYPE_GUEST;
|
||||
}
|
||||
|
||||
@@ -203,11 +284,11 @@ class _Front extends \IPS\Session
|
||||
'current_appcomponent' => ( \IPS\Request::i()->isAjax() ) ? ( $session ? $session['current_appcomponent'] : '' ) : '',
|
||||
'current_module' => ( \IPS\Request::i()->isAjax() ) ? ( $session ? $session['current_module'] : '' ) : '',
|
||||
'current_controller' => ( \IPS\Request::i()->isAjax() ) ? ( $session ? $session['current_controller'] : NULL ) : NULL,
|
||||
'current_id' => ( \IPS\Request::i()->isAjax() ) ? ( $session ? $session['current_id'] : NULL ) : intval( \IPS\Request::i()->id ),
|
||||
'current_id' => ( \IPS\Request::i()->isAjax() ) ? ( $session ? $session['current_id'] : NULL ) : \intval( \IPS\Request::i()->id ),
|
||||
'uagent_key' => $this->userAgent->browser ?: '',
|
||||
'uagent_version' => $this->userAgent->browserVersion ?: '',
|
||||
'uagent_type' => $this->userAgent->spider ? 'search' : 'browser',
|
||||
'search_thread_id' => $session ? intval( $session['search_thread_id'] ) : 0,
|
||||
'search_thread_id' => $session ? \intval( $session['search_thread_id'] ) : 0,
|
||||
'search_thread_time' => $session ? $session['search_thread_time'] : 0,
|
||||
'data' => $session ? $session['data'] : '',
|
||||
'location_url' => $session ? $session['location_url'] : NULL,
|
||||
@@ -262,6 +343,11 @@ class _Front extends \IPS\Session
|
||||
*/
|
||||
public function write( $sessionId, $data )
|
||||
{
|
||||
if ( $this->noWriteGuestSession and empty( $_SESSION['forcedWrite'] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ( !isset( $this->data['data'] ) or $data !== $this->data['data'] or $this->data['member_id'] != $this->member->member_id )
|
||||
{
|
||||
$this->save = TRUE;
|
||||
@@ -383,7 +469,7 @@ class _Front extends \IPS\Session
|
||||
$this->data['current_appcomponent'] = \IPS\Dispatcher::i()->application ? \IPS\Dispatcher::i()->application->directory : '';
|
||||
$this->data['current_module'] = \IPS\Dispatcher::i()->module ? \IPS\Dispatcher::i()->module->key : '';
|
||||
$this->data['current_controller'] = \IPS\Dispatcher::i()->controller;
|
||||
$this->data['current_id'] = intval( \IPS\Request::i()->id );
|
||||
$this->data['current_id'] = \intval( \IPS\Request::i()->id );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,7 +501,7 @@ class _Front extends \IPS\Session
|
||||
$this->data['location_url'] = (string) $url;
|
||||
$this->data['location_lang'] = $lang;
|
||||
$this->data['location_data'] = json_encode( $data );
|
||||
$this->data['current_id'] = intval( \IPS\Request::i()->id );
|
||||
$this->data['current_id'] = \intval( \IPS\Request::i()->id );
|
||||
|
||||
if ( !$this->data['current_appcomponent'] )
|
||||
{
|
||||
@@ -428,7 +514,7 @@ class _Front extends \IPS\Session
|
||||
$groupIds = (string) $groupIds;
|
||||
}
|
||||
|
||||
$groupIds = is_string( $groupIds ) ? explode( ',', $groupIds ) : ( $groupIds ?: NULL );
|
||||
$groupIds = \is_string( $groupIds ) ? explode( ',', $groupIds ) : ( $groupIds ?: NULL );
|
||||
|
||||
$app = \IPS\Application::load( $this->data['current_appcomponent'] );
|
||||
if ( !$app->enabled )
|
||||
@@ -442,7 +528,7 @@ class _Front extends \IPS\Session
|
||||
$groupIds = $groupIds ? array_intersect( $groupIds, explode( ',', $modulePermissions['perm_view'] ) ) : explode( ',', $modulePermissions['perm_view'] );
|
||||
}
|
||||
|
||||
$this->data['location_permissions'] = ( $groupIds !== NULL ) ? ( is_string( $groupIds ) ? $groupIds : implode( ',', $groupIds ) ) : NULL;
|
||||
$this->data['location_permissions'] = ( $groupIds !== NULL ) ? ( \is_string( $groupIds ) ? $groupIds : implode( ',', $groupIds ) ) : NULL;
|
||||
|
||||
$this->save = TRUE;
|
||||
}
|
||||
@@ -496,6 +582,11 @@ class _Front extends \IPS\Session
|
||||
*/
|
||||
public function setType( $type )
|
||||
{
|
||||
if ( $this->data['login_type'] !== $type )
|
||||
{
|
||||
$this->save = TRUE;
|
||||
}
|
||||
|
||||
switch ( $type )
|
||||
{
|
||||
case static::LOGIN_TYPE_MEMBER:
|
||||
@@ -570,4 +661,47 @@ class _Front extends \IPS\Session
|
||||
static::clearSessions( $lifetime );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a session that doesn't require a DB read or write
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function setNoWriteGuestSession()
|
||||
{
|
||||
$this->noWriteGuestSession = TRUE;
|
||||
|
||||
$this->member = new \IPS\Member;
|
||||
|
||||
/* Set data */
|
||||
$this->data = array(
|
||||
'id' => $this->sessionId,
|
||||
'member_name' => '',
|
||||
'seo_name' => '',
|
||||
'member_id' => 0,
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'browser' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '',
|
||||
'running_time' => time(),
|
||||
'login_type' => $this->userAgent->spider ? static::LOGIN_TYPE_SPIDER : static::LOGIN_TYPE_GUEST,
|
||||
'member_group' => \IPS\Settings::i()->guest_group,
|
||||
'current_appcomponent' => '',
|
||||
'current_module' => '',
|
||||
'current_controller' => NULL,
|
||||
'current_id' => \intval( \IPS\Request::i()->id ),
|
||||
'uagent_key' => $this->userAgent->browser ?: '',
|
||||
'uagent_version' => $this->userAgent->browserVersion ?: '',
|
||||
'uagent_type' => $this->userAgent->spider ? 'search' : 'browser',
|
||||
'search_thread_id' => 0,
|
||||
'search_thread_time' => 0,
|
||||
'data' => '',
|
||||
'location_url' => NULL,
|
||||
'location_lang' => NULL,
|
||||
'location_data' => NULL,
|
||||
'location_permissions' => NULL,
|
||||
'theme_id' => isset( \IPS\Request::i()->cookie['theme'] ) ? \IPS\Request::i()->cookie['theme'] : 0,
|
||||
'in_editor' => 0,
|
||||
);
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
+48
-27
@@ -11,7 +11,7 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -47,7 +47,7 @@ abstract class _Session
|
||||
{
|
||||
if( static::$instance === NULL )
|
||||
{
|
||||
$classname = get_called_class();
|
||||
$classname = \get_called_class();
|
||||
|
||||
if ( $classname === 'IPS\Session' )
|
||||
{
|
||||
@@ -105,19 +105,9 @@ abstract class _Session
|
||||
/* Upgrader starts session already */
|
||||
if ( !\IPS\Dispatcher::hasInstance() or !\IPS\Dispatcher::i() instanceof \IPS\Dispatcher\Setup )
|
||||
{
|
||||
if ( version_compare( phpversion(), '5.4.0', '>=' ) )
|
||||
if( session_status() !== PHP_SESSION_ACTIVE )
|
||||
{
|
||||
if( session_status() !== PHP_SESSION_ACTIVE )
|
||||
{
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( session_id() === '' )
|
||||
{
|
||||
session_start();
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,13 +146,16 @@ abstract class _Session
|
||||
{
|
||||
/* PHP 7.0.2 had a bug reported where session_regenerate_id() does not close opened sessions properly and in some situations can cause PHP to hang or crash.
|
||||
* This issue is fixed in PHP 7.1.0 - https://bugs.php.net/bug.php?id=71394 */
|
||||
if ( version_compare( PHP_VERSION, '7.1.0' ) >= 0 )
|
||||
{
|
||||
session_regenerate_id();
|
||||
}
|
||||
session_regenerate_id();
|
||||
|
||||
/* Update our new session id */
|
||||
$this->id = session_id();
|
||||
|
||||
$_SESSION['forcedWrite'] = time();
|
||||
$this->member = $member;
|
||||
|
||||
/* Update CSRF Key based on new data */
|
||||
$this->regenerateCsrfKey();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,21 +168,21 @@ abstract class _Session
|
||||
/* Set ID */
|
||||
$this->id = session_id();
|
||||
|
||||
/* Crate csrf key */
|
||||
$this->csrfKey = md5( "{$this->member->email}& " . ( $this->member->member_id ? $this->member->joined->getTimestamp() : 0 ) . '&' . $this->id );
|
||||
/* Create csrf key */
|
||||
$this->regenerateCsrfKey();
|
||||
|
||||
/* Update member */
|
||||
if ( $this->member->member_id )
|
||||
{
|
||||
$save = FALSE;
|
||||
|
||||
/* Set the last activity */
|
||||
if ( isset( $this->data ) and $this->data['login_type'] != static::LOGIN_TYPE_ANONYMOUS and ! \IPS\Request::i()->isAjax() )
|
||||
/* Set the last activity (but not if this is an ajax request or a partially registered member as we delete where last_visit=0) */
|
||||
if ( isset( $this->data ) and ! \IPS\Request::i()->isAjax() and ( $this->member->email and $this->member->name ) )
|
||||
{
|
||||
if ( time() - $this->member->last_activity > 3600 )
|
||||
if ( time() - $this->member->last_activity > 3600 or !$this->member->last_visit )
|
||||
{
|
||||
$save = TRUE;
|
||||
$this->member->last_visit = $this->member->last_activity;
|
||||
$this->member->last_visit = $this->member->last_activity ?: time();
|
||||
}
|
||||
if ( time() - $this->member->last_activity > 180 )
|
||||
{
|
||||
@@ -199,7 +192,7 @@ abstract class _Session
|
||||
}
|
||||
|
||||
/* Set timezone */
|
||||
if ( !$this->member->members_bitoptions['timezone_override'] and isset( \IPS\Request::i()->cookie['ipsTimezone'] ) and \IPS\Request::i()->cookie['ipsTimezone'] !== $this->member->timezone and in_array( \IPS\Request::i()->cookie['ipsTimezone'], \DateTimeZone::listIdentifiers() ) )
|
||||
if ( isset( \IPS\Request::i()->cookie['ipsTimezone'] ) and \IPS\Request::i()->cookie['ipsTimezone'] !== $this->member->timezone and \in_array( \IPS\Request::i()->cookie['ipsTimezone'], \DateTimeZone::listIdentifiers() ) )
|
||||
{
|
||||
$save = TRUE;
|
||||
$this->member->timezone = \IPS\Request::i()->cookie['ipsTimezone'];
|
||||
@@ -243,7 +236,7 @@ abstract class _Session
|
||||
* @endcode
|
||||
* @param string $langKey Language key for log
|
||||
* @param array $params Key/Values - keys are variables to use in sprintf on $langKey, values are booleans indicating if they are language keys themselves (TRUE) or raw data (FALSE)
|
||||
* @param \IPS\Content\Item|NULL If moderation action is specific to an item
|
||||
* @param \IPS\Content\Item|NULL $item If moderation action is specific to an item
|
||||
* @return void
|
||||
*/
|
||||
public function modLog( $langKey, $params=array(), $item=null )
|
||||
@@ -252,7 +245,7 @@ abstract class _Session
|
||||
|
||||
if ( $item instanceof \IPS\Content\Item )
|
||||
{
|
||||
$class = get_class( $item );
|
||||
$class = \get_class( $item );
|
||||
$idColumn = $class::$databaseColumnId;
|
||||
}
|
||||
|
||||
@@ -271,4 +264,32 @@ abstract class _Session
|
||||
'item_id' => $item ? $item->$idColumn : NULL,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate CSRF Key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function regenerateCsrfKey()
|
||||
{
|
||||
$this->csrfKey = md5( \IPS\SUITE_UNIQUE_KEY . "&{$this->member->email}& " . ( $this->member->member_id ? $this->member->joined->getTimestamp() : 0 ) . '&' . $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum session lifetime (in seconds)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function sessionLifetime()
|
||||
{
|
||||
$timeout = 1440;
|
||||
|
||||
if( \function_exists('ini_get') )
|
||||
{
|
||||
$phpTimeout = @ini_get('session.gc_maxlifetime');
|
||||
$timeout = $phpTimeout ?: $timeout;
|
||||
}
|
||||
|
||||
return $timeout;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -37,7 +37,7 @@ abstract class _Store
|
||||
*/
|
||||
const ONLINE_GUESTS = 4;
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Instance
|
||||
*/
|
||||
protected static $instance = NULL;
|
||||
@@ -51,12 +51,12 @@ abstract class _Store
|
||||
{
|
||||
if ( static::$instance === NULL )
|
||||
{
|
||||
if ( \IPS\REDIS_ENABLED and \IPS\CACHE_METHOD == 'Redis' and \IPS\CACHE_CONFIG )
|
||||
if ( \IPS\REDIS_ENABLED and \IPS\CACHE_METHOD == 'Redis' and ( \IPS\CACHE_CONFIG or \IPS\REDIS_CONFIG ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Try and use Redis */
|
||||
$connection = \IPS\Redis::i()->connection('write');
|
||||
$connection = \IPS\Redis::i()->connection('read');
|
||||
|
||||
if( !$connection )
|
||||
{
|
||||
@@ -113,7 +113,7 @@ abstract class _Store
|
||||
* @param array|NULL $keepSessionIds Array of session ids to keep [optional]
|
||||
* @return void
|
||||
*/
|
||||
abstract public function deleteByMember( $memberId, $userAgent=NULL, $keepSessionIds=NULL );
|
||||
abstract public function deleteByMember( int $memberId, string $userAgent=NULL, array $keepSessionIds=NULL );
|
||||
|
||||
/**
|
||||
* Delete from the session engine
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session\Store;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -96,7 +96,7 @@ class _Database extends \IPS\Session\Store
|
||||
* @param array|NULL $keepSessionIds Array of session ids to keep [optional]
|
||||
* @return void
|
||||
*/
|
||||
public function deleteByMember( $memberId, $userAgent=NULL, $keepSessionIds=NULL )
|
||||
public function deleteByMember( int $memberId, string $userAgent=NULL, array $keepSessionIds=NULL )
|
||||
{
|
||||
$where = array( array( 'member_id=?', $memberId ) );
|
||||
|
||||
@@ -105,7 +105,7 @@ class _Database extends \IPS\Session\Store
|
||||
$where[] = array( 'browser=?', $userAgent );
|
||||
}
|
||||
|
||||
if ( $keepSessionIds )
|
||||
if ( \is_array( $keepSessionIds ) AND \count( $keepSessionIds ) )
|
||||
{
|
||||
$where[] = array( \IPS\Db::i()->in( 'id', $keepSessionIds, TRUE ) );
|
||||
}
|
||||
@@ -172,9 +172,16 @@ class _Database extends \IPS\Session\Store
|
||||
|
||||
if ( ! $showAnonymous )
|
||||
{
|
||||
$where[] = array( "s.login_type!=?", \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS );
|
||||
if( \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
$where[] = array( "(s.login_type!=? OR s.member_id=?)", \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS, \IPS\Member::loggedIn()->member_id );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( "s.login_type!=?", \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ! $flags and ! $limit )
|
||||
{
|
||||
/* Simple query for PHP processing */
|
||||
@@ -183,10 +190,20 @@ class _Database extends \IPS\Session\Store
|
||||
else
|
||||
{
|
||||
/* Complex group by mode with all the lovely trimmings yum */
|
||||
$subWhere = $where;
|
||||
$guestSubWhere = $where;
|
||||
$guestSubWhere[] = 's.member_id IS NULL';
|
||||
|
||||
$memberSubWhere = $where;
|
||||
$memberSubWhere[] = 's.member_id IS NOT NULL';
|
||||
|
||||
/* Ok, this looks odd, but the ONLY_FULL_GROUP_BY bites us here, so selecting max(id) allows us to return a session ID even though we're grouping on member_id */
|
||||
$where = array( array( "( core_sessions.id IN(?) OR ( core_sessions.member_id IS NULL AND core_sessions.login_type != ? AND core_sessions.running_time > ?) )", \IPS\Db::i()->select( 'MAX(id)', array( 'core_sessions', 's' ), $subWhere, NULL, NULL, 'member_id' ), \IPS\Session\Front::LOGIN_TYPE_SPIDER, \IPS\DateTime::create()->sub( new \DateInterval( 'PT30M' ) )->getTimeStamp() ) );
|
||||
$where = array(
|
||||
array(
|
||||
"( core_sessions.id IN(?) OR core_sessions.id IN(?) )",
|
||||
\IPS\Db::i()->select( 'MAX(id)', array( 'core_sessions', 's' ), $memberSubWhere, NULL, NULL, 'member_id' ),
|
||||
\IPS\Db::i()->select( 'MAX(id)', array( 'core_sessions', 's' ), $guestSubWhere, NULL, NULL, 'ip_address' )
|
||||
)
|
||||
);
|
||||
|
||||
/* Limiting to a user group? */
|
||||
if ( $memberGroup )
|
||||
@@ -243,7 +260,7 @@ class _Database extends \IPS\Session\Store
|
||||
{
|
||||
$where[] = array( 'core_sessions.current_id = ?', \IPS\Request::i()->id );
|
||||
}
|
||||
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'core_sessions.member_id,core_sessions.member_name,core_sessions.seo_name,core_sessions.member_group,core_sessions.login_type,core_sessions.in_editor', 'core_sessions', $where, 'core_sessions.running_time DESC' ) as $row )
|
||||
{
|
||||
if( $row['login_type'] == \IPS\Session\Front::LOGIN_TYPE_MEMBER and $row['member_name'] )
|
||||
|
||||
+158
-53
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session\Store;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -35,7 +35,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function loadSession( $sessionId )
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( $this->_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) ) )
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( static::_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) ) )
|
||||
{
|
||||
return \IPS\Redis::i()->decode( $result['data'] );
|
||||
}
|
||||
@@ -55,8 +55,8 @@ class _Redis extends \IPS\Session\Store
|
||||
$group = \IPS\Member\Group::load( $data['member_group'] );
|
||||
|
||||
/* Update the specific session */
|
||||
\IPS\Redis::i()->delete( $this->_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->hMSet( $this->_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ), array(
|
||||
\IPS\Redis::i()->del( static::_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->hMSet( static::_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ), array(
|
||||
'member_id' => $data['member_id'],
|
||||
'member_name' => $data['member_name'],
|
||||
'seo_name' => $data['seo_name'],
|
||||
@@ -67,35 +67,35 @@ class _Redis extends \IPS\Session\Store
|
||||
), static::$ttl );
|
||||
|
||||
/* Update the list of sessions for the online list [ microtime => sessionID ] */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_map' ), time(), 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_map' ), time(), 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
|
||||
/* Update users list */
|
||||
if ( $data['uagent_type'] == 'search' )
|
||||
{
|
||||
/* Make a unique row based on IP and user-agent to prevent multiple rows for each spider */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_spiders' ), time(), md5( $data['ip_address'] . $data['browser'] ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_spiders' ), time(), md5( $data['ip_address'] . $data['browser'] ), static::$ttl );
|
||||
}
|
||||
else if ( $data['member_id'] )
|
||||
{
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_users' ), time(), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_users' ), time(), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* A guest may have one ip address but multiple devices, but we don't really need to track that */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_guests' ), time(), md5( $data['ip_address'] ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_guests' ), time(), md5( $data['ip_address'] ), static::$ttl );
|
||||
}
|
||||
|
||||
/* Delete old items */
|
||||
if ( ! \IPS\Redis::i()->get( $this->_key( 'session_cleanup' ) ) )
|
||||
if ( ! \IPS\Redis::i()->get( static::_key( 'session_cleanup' ) ) )
|
||||
{
|
||||
/* Do a little clean up */
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_map' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_spiders' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_users' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_guests' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_map' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_spiders' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_users' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_guests' ), 0, time() - static::$ttl );
|
||||
|
||||
/* And do it again in 3ish mins */
|
||||
\IPS\Redis::i()->setEx( $this->_key( 'session_cleanup' ), 180, time() );
|
||||
\IPS\Redis::i()->setEx( static::_key( 'session_cleanup' ), 180, time() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,11 @@ class _Redis extends \IPS\Session\Store
|
||||
public function deleteSession( $sessionId )
|
||||
{
|
||||
$data = $this->loadSession( $sessionId );
|
||||
\IPS\Redis::i()->delete( $this->_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_map' ), 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_spiders' ), md5( $data['ip_address'] . $data['browser'] ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_users' ), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_guests' ), md5( $data['ip_address'] ) );
|
||||
\IPS\Redis::i()->del( static::_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_map' ), 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_spiders' ), md5( $data['ip_address'] . $data['browser'] ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_users' ), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_guests' ), md5( $data['ip_address'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,14 +123,14 @@ class _Redis extends \IPS\Session\Store
|
||||
* @param array|NULL $keepSessionIds Array of session ids to keep [optional]
|
||||
* @return void
|
||||
*/
|
||||
public function deleteByMember( $memberId, $userAgent=NULL, $keepSessionIds=NULL )
|
||||
public function deleteByMember( int $memberId, string $userAgent=NULL, array $keepSessionIds=NULL )
|
||||
{
|
||||
if ( ! is_array( $keepSessionIds ) )
|
||||
if ( ! \is_array( $keepSessionIds ) and ! \is_null( $keepSessionIds ) )
|
||||
{
|
||||
$keepSessionIds = array( $keepSessionIds );
|
||||
}
|
||||
|
||||
$sessionMap = \IPS\Redis::i()->zRange( 'session_map', 0, -1 );
|
||||
$sessionMap = \IPS\Redis::i()->zRange( static::_key( 'session_map' ), 0, -1 );
|
||||
|
||||
foreach( $sessionMap as $index => $redisKey )
|
||||
{
|
||||
@@ -151,8 +151,8 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$delete = false;
|
||||
}
|
||||
|
||||
if ( $keepSessionIds and in_array( $session['id'], $keepSessionIds ) )
|
||||
|
||||
if ( $keepSessionIds and \is_array( $keepSessionIds ) and \in_array( $session['id'], $keepSessionIds ) )
|
||||
{
|
||||
$delete = false;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getSessionIds()
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->zRangeByScore( 'session_map', '-inf', '+inf', array( 'withscores' => false ) ) )
|
||||
if ( $result = \IPS\Redis::i()->zRangeByScore( static::_key( 'session_map' ), '-inf', '+inf', array( 'withscores' => false ) ) )
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getLatestMemberSession( $memberId )
|
||||
{
|
||||
$redis = \IPS\Redis::i()->zRevRangeByScore( 'session_online_users', '+inf', '-inf', array('withscores' => FALSE, 'alpha' => TRUE ) );
|
||||
$redis = \IPS\Redis::i()->zRevRangeByScore( static::_key( 'session_online_users' ), '+inf', '-inf', array('withscores' => FALSE, 'alpha' => TRUE ) );
|
||||
|
||||
foreach( $redis as $data )
|
||||
{
|
||||
@@ -195,7 +195,7 @@ class _Redis extends \IPS\Session\Store
|
||||
|
||||
if ( $id == $memberId )
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( $this->_key( $sessionKey ) ) )
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( static::_key( $sessionKey ) ) )
|
||||
{
|
||||
return \IPS\Redis::i()->decode( $result['data'] );
|
||||
}
|
||||
@@ -214,23 +214,64 @@ class _Redis extends \IPS\Session\Store
|
||||
public static function clearSessions( $timeout )
|
||||
{
|
||||
/* Remove the public facing items. This is only called by PHP's session gc so individual sessions do not need removing as they are cleaned by Redis' TTL */
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_map', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_spiders', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_users', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_guests', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_map' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_spiders' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_users' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_guests' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->del( static::_key( 'session_onlinelist' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis key
|
||||
*/
|
||||
protected static $_redisKey;
|
||||
|
||||
/**
|
||||
* Returns a key to be stored with Redis
|
||||
*
|
||||
* @param string $key Key suffix
|
||||
* @return string
|
||||
*/
|
||||
protected function _key( $key )
|
||||
protected static function _key( $key )
|
||||
{
|
||||
return $key;
|
||||
/* Only manage the session_onlist key which is prone to corruption. We don't want to wipe out the online list each time this file fails */
|
||||
if ( $key == 'session_onlinelist' )
|
||||
{
|
||||
if ( !static::$_redisKey )
|
||||
{
|
||||
/* Last access ensures that the data is not stale if we fail back to MySQL and then go back to Redis later */
|
||||
if ( !( static::$_redisKey = \IPS\Redis::i()->get( 'redisKey_session' ) ) OR ! \IPS\Redis::i()->get( 'redisStore_lastAccess' ) )
|
||||
{
|
||||
static::$_redisKey = md5( mt_rand() );
|
||||
\IPS\Redis::i()->setex( 'redisKey_session', 604800, static::$_redisKey );
|
||||
\IPS\Redis::i()->setex( 'redisStore_lastAccess', ( 3 * 3600 ), time() );
|
||||
}
|
||||
}
|
||||
|
||||
return static::$_redisKey . '_' . $key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resets the session key to force ignore any previous redis data stored
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function resetKey()
|
||||
{
|
||||
static::$_redisKey = md5( mt_rand() );
|
||||
\IPS\Redis::i()->setex( 'redisKey_session', 604800, static::$_redisKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis key
|
||||
*/
|
||||
protected $fetchAttempt = 0;
|
||||
|
||||
/**
|
||||
* Fetch all online users (but not spiders)
|
||||
*
|
||||
@@ -243,36 +284,83 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getOnlineUsers( $flags=0, $sort='desc', $limit=NULL, $memberGroup=NULL, $showAnonymous=FALSE )
|
||||
{
|
||||
$results = \IPS\Redis::i()->lRange('session_onlinelist', 0, -1 );
|
||||
|
||||
try
|
||||
{
|
||||
$results = \IPS\Redis::i()->lRange( static::_key( 'session_onlinelist' ), 0, -1 );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
|
||||
$results = NULL;
|
||||
}
|
||||
|
||||
if ( ! $results )
|
||||
{
|
||||
/* Ensure file is deleted */
|
||||
try
|
||||
{
|
||||
\IPS\Redis::i()->del( static::_key( 'session_onlinelist' ) );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'sort' => $sort === 'asc' ? 'asc' : 'desc',
|
||||
'store' => \IPS\Redis::i()->prefix . 'session_onlinelist',
|
||||
'store' => \IPS\Redis::i()->prefix . static::_key( 'session_onlinelist' ),
|
||||
'alpha' => true,
|
||||
'by' => 'nosort ' . $sort === 'asc' ? 'asc' : 'desc',
|
||||
'ttl' => 30, /* This ensures the stored file session_online only lasts for 30 seconds */
|
||||
'get' => array(
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_id',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_name',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->seo_name',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_group',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->login_type',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->data'
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_id',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_name',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->seo_name',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_group',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->login_type',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->data'
|
||||
)
|
||||
);
|
||||
|
||||
\IPS\Redis::i()->sort( $this->_key( 'session_map' ), $options );
|
||||
\IPS\Redis::i()->sort( static::_key( 'session_map' ), $options );
|
||||
|
||||
$results = \IPS\Redis::i()->lRange('session_onlinelist', 0, -1 );
|
||||
try
|
||||
{
|
||||
/* Force expiration in 30 seconds to prevent stale caches hanging around */
|
||||
\IPS\Redis::i()->expire( static::_key( 'session_onlinelist' ), 30 );
|
||||
|
||||
$results = \IPS\Redis::i()->lRange( static::_key( 'session_onlinelist' ), 0, -1 );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
$this->fetchAttempt++;
|
||||
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
|
||||
if ( $this->fetchAttempt < 2 )
|
||||
{
|
||||
/* And try again, but only once more to prevent an infinite loop */
|
||||
return $this->getOnlineUsers( $flags, $sort, $limit, $memberGroup, $showAnonymous );
|
||||
}
|
||||
else
|
||||
{
|
||||
$results = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the fetch attempt */
|
||||
$this->fetchAttempt = 0;
|
||||
|
||||
/* Sort returns a flat array, so [ 1, matt, matt, 4, 0, 2, Joe, joe, 3, 0 .. ] so we need to build that into an associative array we can work with */
|
||||
$return = array();
|
||||
$i = 0;
|
||||
|
||||
while( $i < count( $results ) )
|
||||
while( $i < \count( $results ) )
|
||||
{
|
||||
$fields = array();
|
||||
$data = NULL;
|
||||
@@ -282,13 +370,18 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$data = \IPS\Redis::i()->decode( $results[ $i++ ] );
|
||||
}
|
||||
/* login_type must be cast as an integer or else anonymous state can be lost when adjustSessions() runs */
|
||||
elseif( $field === 'login_type' )
|
||||
{
|
||||
$fields[ $field ] = (int) $results[ $i++ ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$fields[ $field ] = $results[ $i++ ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array( $data ) AND count( $data ) )
|
||||
if ( \is_array( $data ) AND \count( $data ) )
|
||||
{
|
||||
/* Have we already fetched this member? */
|
||||
if ( $fields['member_id'] and isset( $return[ $fields['member_id'] ] ) )
|
||||
@@ -301,7 +394,13 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Ignore spiders */
|
||||
if ( ! $fields['member_id'] and ! ( $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_GUEST or $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_INCOMPLETE ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[ $fields['member_id'] ? $fields['member_id'] : $data['ip_address'] ] = array_merge( $data, $fields );
|
||||
}
|
||||
}
|
||||
@@ -333,7 +432,7 @@ class _Redis extends \IPS\Session\Store
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $showAnonymous and $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS )
|
||||
if ( ! $showAnonymous and $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS and ( !\IPS\Member::loggedIn()->member_id OR $data['member_id'] != \IPS\Member::loggedIn()->member_id ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -344,12 +443,18 @@ class _Redis extends \IPS\Session\Store
|
||||
/* Count only? */
|
||||
if ( $flags & static::ONLINE_COUNT_ONLY )
|
||||
{
|
||||
return count( $members );
|
||||
return \count( $members );
|
||||
}
|
||||
|
||||
/* Hooray for PHP 7 */
|
||||
usort($members, function ($m1, $m2 )
|
||||
{
|
||||
return $m2['running_time'] <=> $m1['running_time'];
|
||||
} );
|
||||
|
||||
if ( $limit )
|
||||
{
|
||||
return array_slice( $members, $limit[0], $limit[1], TRUE );
|
||||
return \array_slice( $members, $limit[0], $limit[1], TRUE );
|
||||
}
|
||||
|
||||
return $members;
|
||||
@@ -372,9 +477,9 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$members = array();
|
||||
|
||||
foreach( $this->getOnlineUsers( 0, 'desc' ) as $member )
|
||||
foreach( $this->getOnlineUsers( static::ONLINE_MEMBERS, 'desc' ) as $member )
|
||||
{
|
||||
if ( $member['current_appcomponent'] == $app and $member['current_module'] == $module and $member['current_controller'] = $controller and $member['current_id'] = $id )
|
||||
if ( $member['current_appcomponent'] == $app and $member['current_module'] == $module and $member['current_controller'] == $controller and $member['current_id'] == $id )
|
||||
{
|
||||
if ( $url and mb_stristr( $member['location_url'], $url ) )
|
||||
{
|
||||
|
||||
Whitespace-only changes.
Reference in new issue
Block a user