Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Fri, Feb 21, 11:58 PM

in-portal

Index: branches/5.2.x/core/kernel/session/session_storage.php
===================================================================
--- branches/5.2.x/core/kernel/session/session_storage.php (revision 16792)
+++ branches/5.2.x/core/kernel/session/session_storage.php (revision 16793)
@@ -1,552 +1,557 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Implements Session Store in the Database
*
*/
class SessionStorage extends kDBBase {
/**
* Reference to session
*
* @var Session
* @access protected
*/
protected $Session = null;
var $Expiration;
var $SessionTimeout = 0;
var $DirectVars = Array ();
var $ChangedDirectVars = Array ();
var $PersistentVars = Array ();
/**
* Default persistent vars
*
* @var array
*/
protected $defaultPersistentVars = array();
var $OriginalData = Array ();
var $TimestampField;
var $SessionDataTable;
var $DataValueField;
var $DataVarField;
public function Init($prefix, $special)
{
parent::Init($prefix, $special);
$this->TableName = 'sessions';
$this->IDField = 'sid';
$this->TimestampField = 'expire';
$this->SessionDataTable = 'SessionData';
$this->DataValueField = 'value';
$this->DataVarField = 'var';
}
/**
* Sets reference to session
*
* @param Session $session
*/
public function setSession(&$session)
{
$this->Session =& $session;
$this->SessionTimeout = $session->SessionTimeout;
}
/**
* Calculates browser signature
*
* @return string
*/
function _getBrowserSignature()
{
$signature_parts = Array(
'HTTP_USER_AGENT', 'SERVER_PROTOCOL',
'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_LANGUAGE'
);
$ret = '';
foreach ($signature_parts as $signature_part) {
if (array_key_exists($signature_part, $_SERVER)) {
$ret .= '&|&' . $_SERVER[$signature_part];
}
}
return md5( substr($ret, 3) );
}
function GetSessionDefaults()
{
$fields_hash = Array (
$this->IDField => $this->Session->SID,
$this->TimestampField => $this->Session->Expiration,
);
if (!defined('IS_INSTALL') || !IS_INSTALL) {
// this column was added only in 5.0.1 version,
// so accessing it while database is not upgraded
// will result in admin's inability to login inside
// installator
$fields_hash['BrowserSignature'] = $this->_getBrowserSignature();
}
// default values + values set during this script run
return array_merge($fields_hash, $this->DirectVars);
}
/**
* Stores session to database
*
* @param bool $to_database
*
* @return void
* @access public
*/
public function StoreSession($to_database = true)
{
if ( defined('IS_INSTALL') && IS_INSTALL && $to_database && !$this->Application->TableFound($this->TableName, true) ) {
return;
}
$fields_hash = $this->GetSessionDefaults();
if ( $to_database ) {
$this->Conn->doInsert($fields_hash, $this->TableName);
}
foreach ($fields_hash as $field_name => $field_value) {
$this->SetField($field_name, $field_value);
}
+ // Reset change flags, because session is already saved to the database.
+ $this->ChangedDirectVars = array();
+
// ensure user groups are stored in a way, that kPermissionsHelper::CheckUserPermission can understand
$this->Session->StoreVar('UserGroups', $this->GetField('GroupList'), !$to_database);
}
function DeleteSession()
{
$this->DeleteSessions( Array ($this->Session->SID), SESSION_LOG_LOGGED_OUT );
$this->DirectVars = $this->ChangedDirectVars = $this->OriginalData = Array();
}
function UpdateSession($timeout = 0)
{
$this->SetField($this->TimestampField, $this->Session->Expiration);
$query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$this->Session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($this->Session->SID);
$this->Conn->Query($query);
}
function LocateSession($sid)
{
$sql = 'SELECT *
FROM ' . $this->TableName . '
WHERE ' . $this->IDField . ' = ' . $this->Conn->qstr($sid);
$result = $this->Conn->GetRow($sql);
if ($result === false) {
return false;
}
// perform security checks to ensure, that session is used by it's creator
if ($this->Application->ConfigValue('SessionBrowserSignatureCheck') && ($result['BrowserSignature'] != $this->_getBrowserSignature()) && $this->Application->GetVar('flashsid') === false) {
return false;
}
if ($this->Application->ConfigValue('SessionIPAddressCheck') && ($result['IpAddress'] != $this->Application->getClientIp())) {
// most secure, except for cases where NAT (Network Address Translation)
// is used and two or more computers can have same IP address
return false;
}
$this->DirectVars = $result;
$this->Expiration = $result[$this->TimestampField];
return true;
}
function GetExpiration()
{
return $this->Expiration;
}
function LoadData()
{
$query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($this->Session->SID);
$this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField);
return $this->OriginalData;
}
/**
* Enter description here...
*
* @param string $var_name
* @param mixed $default
* @return mixed
*/
function GetField($var_name, $default = false)
{
return isset($this->DirectVars[$var_name]) ? $this->DirectVars[$var_name] : $default;
//return $this->Conn->GetOne('SELECT '.$var_name.' FROM '.$this->TableName.' WHERE `'.$this->IDField.'` = '.$this->Conn->qstr($this->Session->GetID()) );
}
function SetField($var_name, $value)
{
$value_changed = !isset($this->DirectVars[$var_name]) || ($this->DirectVars[$var_name] != $value);
if ($value_changed) {
$this->DirectVars[$var_name] = $value;
$this->ChangedDirectVars[] = $var_name;
$this->ChangedDirectVars = array_unique($this->ChangedDirectVars);
}
//return $this->Conn->Query('UPDATE '.$this->TableName.' SET '.$var_name.' = '.$this->Conn->qstr($value).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($this->Session->GetID()) );
}
/**
* Saves changes in session to database using single REPLACE query
*
* @return void
* @access public
*/
public function SaveData()
{
if ( !$this->Session->SID ) {
// can't save without sid
return ;
}
$replace = '';
$ses_data = $this->Session->Data->GetParams();
foreach ($ses_data as $key => $value) {
if ( isset($this->OriginalData[$key]) && $this->OriginalData[$key] == $value ) {
continue; //skip unchanged session data
}
else {
$replace .= sprintf("(%s, %s, %s),", $this->Conn->qstr($this->Session->SID), $this->Conn->qstr($key), $this->Conn->qstr($value));
}
}
$replace = rtrim($replace, ',');
if ( $replace != '' ) {
$query = ' REPLACE INTO ' . $this->SessionDataTable . ' (' . $this->IDField . ', ' . $this->DataVarField . ', ' . $this->DataValueField . ') VALUES ' . $replace;
$this->Conn->Query($query);
+ $this->OriginalData = $ses_data;
}
if ( $this->ChangedDirectVars ) {
$changes = Array ();
foreach ($this->ChangedDirectVars as $var) {
$changes[] = $var . ' = ' . $this->Conn->qstr($this->DirectVars[$var]);
}
$query = ' UPDATE ' . $this->TableName . '
SET ' . implode(',', $changes) . '
WHERE ' . $this->IDField . ' = ' . $this->Conn->qstr($this->Session->GetID());
$this->Conn->Query($query);
+ $this->ChangedDirectVars = array();
}
}
function RemoveFromData($var)
{
if ($this->Session->SessionSet) {
// only, when session is stored in database
$sql = 'DELETE FROM ' . $this->SessionDataTable . '
WHERE ' . $this->IDField . ' = ' . $this->Conn->qstr($this->Session->SID) . ' AND ' . $this->DataVarField . ' = ' . $this->Conn->qstr($var);
$this->Conn->Query($sql);
}
unset($this->OriginalData[$var]);
}
function GetFromData($var, $default = false)
{
return array_key_exists($var, $this->OriginalData) ? $this->OriginalData[$var] : $default;
}
function GetExpiredSIDs()
{
$sql = 'SELECT ' . $this->IDField . '
FROM ' . $this->TableName . '
WHERE ' . $this->TimestampField . ' > ' . adodb_mktime();
return $this->Conn->GetCol($sql);
}
function DeleteExpired()
{
$expired_sids = $this->GetExpiredSIDs();
$this->DeleteSessions($expired_sids);
return $expired_sids;
}
function DeleteSessions($session_ids, $delete_reason = SESSION_LOG_EXPIRED)
{
if (!$session_ids) {
return ;
}
$log_table = $this->Application->getUnitOption('session-log', 'TableName');
if ($log_table) {
// mark session with proper status
$sub_sql = 'SELECT ' . $this->TimestampField . ' - ' . $this->SessionTimeout . '
FROM ' . $this->TableName . '
WHERE ' . $this->IDField . ' = ' . $log_table . '.SessionId';
$sql = 'UPDATE ' . $log_table . '
SET Status = ' . $delete_reason . ', SessionEnd = (' . $sub_sql . ')
WHERE Status = ' . SESSION_LOG_ACTIVE . ' AND SessionId IN (' . implode(',', $session_ids) . ')';
$this->Conn->Query($sql);
}
$where_clause = ' WHERE ' . $this->IDField . ' IN (' . implode(',', $session_ids) . ')';
$sql = 'DELETE FROM ' . $this->SessionDataTable . $where_clause;
$this->Conn->Query($sql);
$sql = 'DELETE FROM ' . $this->TableName . $where_clause;
$this->Conn->Query($sql);
// delete debugger ouputs left of deleted sessions
foreach ($session_ids as $session_id) {
$debug_file = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache') . '/debug_@' . $session_id . '@.txt';
if (file_exists($debug_file)) {
@unlink($debug_file);
}
}
}
function LoadPersistentVars()
{
$user_id = $this->Session->RecallVar('user_id');
if ( $user_id != USER_GUEST ) {
// Root & normal users.
$this->PersistentVars = $this->getPersistentVars($user_id);
}
else {
$this->PersistentVars = Array ();
}
$default_user_id = (int)$this->Application->ConfigValue('DefaultSettingsUserId');
if ( !$default_user_id ) {
$default_user_id = USER_ROOT;
}
if ( $user_id != $default_user_id ) {
$this->defaultPersistentVars = $this->getPersistentVars($default_user_id);
}
}
/**
* Get persistent vars
*
* @param integer $user_id User Id.
*
* @return array
*/
protected function getPersistentVars($user_id)
{
$sql = 'SELECT VariableValue, VariableName
FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE PortalUserId = ' . $user_id;
return $this->Conn->GetCol($sql, 'VariableName');
}
/**
* Stores variable to persistent session
*
* @param string $var_name
* @param mixed $var_value
* @param bool $optional
* @return void
* @access public
*/
public function StorePersistentVar($var_name, $var_value, $optional = false)
{
$user_id = $this->Session->RecallVar('user_id');
if ( $user_id == USER_GUEST || $user_id === false ) {
// -2 (when not logged in), false (when after u:OnLogout event)
$this->Session->StoreVar($var_name, $var_value, $optional);
return;
}
$this->PersistentVars[$var_name] = $var_value;
$key_clause = 'PortalUserId = ' . $user_id . ' AND VariableName = ' . $this->Conn->qstr($var_name);
$sql = 'SELECT VariableName
FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE ' . $key_clause;
$record_found = $this->Conn->GetOne($sql);
$fields_hash = Array (
'PortalUserId' => $user_id,
'VariableName' => $var_name,
'VariableValue' => $var_value,
);
if ( $record_found ) {
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData', $key_clause);
}
else {
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData');
}
}
/**
* Gets persistent variable
*
* @param string $var_name
* @param mixed $default
* @return mixed
* @access public
*/
public function RecallPersistentVar($var_name, $default = false)
{
if ( $this->Session->RecallVar('user_id') == USER_GUEST ) {
if ( $default == ALLOW_DEFAULT_SETTINGS ) {
$default = null;
}
return $this->Session->RecallVar($var_name, $default);
}
if ( array_key_exists($var_name, $this->PersistentVars) ) {
return $this->PersistentVars[$var_name];
}
elseif ( $default == ALLOW_DEFAULT_SETTINGS ) {
if ( isset($this->defaultPersistentVars[$var_name]) ) {
$value = $this->defaultPersistentVars[$var_name];
$this->StorePersistentVar($var_name, $value);
return $value;
}
$this->PersistentVars[$var_name] = false;
return false;
}
return $default;
}
/**
* Removes variable from persistent session
*
* @param string $var_name
* @return void
* @access public
*/
function RemovePersistentVar($var_name)
{
unset($this->PersistentVars[$var_name]);
$user_id = $this->Session->RecallVar('user_id');
if ( $user_id == USER_GUEST || $user_id === false ) {
// -2 (when not logged in), false (when after u:OnLogout event)
$this->Session->RemoveVar($var_name);
}
else {
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE PortalUserId = ' . $user_id . ' AND VariableName = ' . $this->Conn->qstr($var_name);
$this->Conn->Query($sql);
}
}
/**
* Checks of object has given field
*
* @param string $name
*
* @return bool
* @access public
* @throws BadMethodCallException Always.
*/
public function HasField($name)
{
throw new BadMethodCallException('Unsupported');
}
/**
* Returns field values
*
* @return Array
* @access public
* @throws BadMethodCallException Always.
*/
public function GetFieldValues()
{
throw new BadMethodCallException('Unsupported');
}
/**
* Returns unformatted field value
*
* @param string $field
*
* @return string
* @access public
* @throws BadMethodCallException Always.
*/
public function GetDBField($field)
{
throw new BadMethodCallException('Unsupported');
}
/**
* Returns true, when list/item was queried/loaded
*
* @return bool
* @access public
* @throws BadMethodCallException Always.
*/
public function isLoaded()
{
throw new BadMethodCallException('Unsupported');
}
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
*
* @return Array
* @access public
* @throws BadMethodCallException Always.
*/
public function GetCol($field)
{
throw new BadMethodCallException('Unsupported');
}
}
Index: branches/5.2.x/core/units/helpers/user_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/user_helper.php (revision 16792)
+++ branches/5.2.x/core/units/helpers/user_helper.php (revision 16793)
@@ -1,753 +1,755 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class UserHelper extends kHelper {
/**
* Event to be used during login processings
*
* @var kEvent
*/
var $event = null;
/**
* Performs user login and returns the result
*
* @param string $username
* @param string $password
* @param bool $dry_run
* @param bool $remember_login
* @param string $remember_login_cookie
* @return int
*/
function loginUser($username, $password, $dry_run = false, $remember_login = false, $remember_login_cookie = '')
{
if ( !isset($this->event) ) {
$this->event = new kEvent('u:OnLogin');
}
if ( (!$username || !$password) && !$remember_login_cookie ) {
return LoginResult::INVALID_PASSWORD;
}
$object =& $this->getUserObject();
// process "Save Username" checkbox
if ( $this->Application->isAdmin ) {
$save_username = $this->Application->GetVar('cb_save_username') ? $username : '';
$this->Application->Session->SetCookie('save_username', $save_username, strtotime('+1 year'));
// cookie will be set on next refresh, but refresh won't occur if
// login error present, so duplicate cookie in kHTTPQuery
$this->Application->SetVar('save_username', $save_username);
}
// logging in "root" (admin only)
$super_admin = ($username == 'super-root') && $this->verifySuperAdmin();
if ( $this->Application->isAdmin && ($username == 'root') || ($super_admin && $username == 'super-root') ) {
/** @var kPasswordFormatter $password_formatter */
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
if ( !$password_formatter->checkPasswordFromSetting('RootPass', $password) ) {
return LoginResult::INVALID_PASSWORD;
}
$user_id = USER_ROOT;
$object->Clear($user_id);
$object->SetDBField('Username', 'root');
if ( !$dry_run ) {
$this->loginUserById($user_id, $remember_login_cookie);
if ( $super_admin ) {
$this->Application->StoreVar('super_admin', 1);
}
// reset counters
$this->Application->resetCounters('UserSessions');
$this->_processLoginRedirect('root', $password);
$this->_processInterfaceLanguage();
$this->_fixNextTemplate();
}
return LoginResult::OK;
}
$user_id = $this->getUserId($username, $password, $remember_login_cookie);
if ( $user_id ) {
$object->Load($user_id);
if ( !$this->checkBanRules($object) ) {
return LoginResult::BANNED;
}
if ( $object->GetDBField('Status') == STATUS_ACTIVE ) {
if ( !$this->checkLoginPermission() ) {
return LoginResult::NO_PERMISSION;
}
if ( !$dry_run ) {
$this->loginUserById($user_id, $remember_login_cookie);
if ( $remember_login ) {
// remember username & password when "Remember Login" checkbox us checked (when user is using login form on Front-End)
$sql = 'SELECT MD5(Password)
FROM ' . TABLE_PREFIX . 'Users
WHERE PortalUserId = ' . $user_id;
$remember_login_hash = $this->Conn->GetOne($sql);
$this->Application->Session->SetCookie('remember_login', $username . '|' . $remember_login_hash, strtotime('+1 month'));
}
if ( !$remember_login_cookie ) {
// reset counters
$this->Application->resetCounters('UserSessions');
$this->_processLoginRedirect($username, $password);
$this->_processInterfaceLanguage();
$this->_fixNextTemplate();
}
}
return LoginResult::OK;
}
else {
$pending_template = $this->Application->GetVar('pending_disabled_template');
if ( $pending_template !== false && !$dry_run ) {
// when user found, but it's not yet approved redirect hit to notification template
$this->event->redirect = $pending_template;
return LoginResult::OK;
}
else {
// when no notification template given return an error
return LoginResult::INVALID_PASSWORD;
}
}
}
if ( !$dry_run ) {
$this->event->SetRedirectParam('pass', 'all');
// $this->event->SetRedirectParam('pass_category', 1); // to test
}
return LoginResult::INVALID_PASSWORD;
}
/**
* Login user by it's id
*
* @param int $user_id
* @param bool $remember_login_cookie
*/
function loginUserById($user_id, $remember_login_cookie = false)
{
$object =& $this->getUserObject();
$this->Application->SetVar('u.current_id', $user_id);
if ( !$this->Application->isAdmin ) {
// needed for "profile edit", "registration" forms ON FRONT ONLY
$this->Application->SetVar('u_id', $user_id);
}
$this->Application->StoreVar('user_id', $user_id);
$this->Application->Session->SetField('PortalUserId', $user_id);
if ($user_id != USER_ROOT) {
$groups = $this->Application->RecallVar('UserGroups');
list ($first_group, ) = explode(',', $groups);
$this->Application->Session->SetField('GroupId', $first_group);
$this->Application->Session->SetField('GroupList', $groups);
$this->Application->Session->SetField('TimeZone', $object->GetDBField('TimeZone'));
}
$this->Application->LoadPersistentVars();
if (!$remember_login_cookie) {
// don't change last login time when auto-login is used
$this_login = (int)$this->Application->RecallPersistentVar('ThisLogin');
$this->Application->StorePersistentVar('LastLogin', $this_login);
$this->Application->StorePersistentVar('ThisLogin', adodb_mktime());
}
+ $this->Application->Session->SaveData();
+
$hook_event = new kEvent('u:OnAfterLogin');
$hook_event->MasterEvent = $this->event;
$this->Application->HandleEvent($hook_event);
}
/**
* Checks login permission
*
* @return bool
*/
function checkLoginPermission()
{
$object =& $this->getUserObject();
$ip_restrictions = $object->GetDBField('IPRestrictions');
if ( $ip_restrictions && !$this->Application->isDebugMode() && !kUtil::ipMatch($ip_restrictions, "\n") ) {
return false;
}
$groups = $object->getMembershipGroups(true);
if ( !$groups ) {
$groups = Array ();
}
$default_group = $this->getUserTypeGroup();
if ( $default_group !== false ) {
array_push($groups, $default_group);
}
// store groups, because kApplication::CheckPermission will use them!
array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup'));
$groups = array_unique($groups);
$this->Application->StoreVar('UserGroups', implode(',', $groups), true); // true for optional
return $this->Application->CheckPermission($this->Application->isAdmin ? 'ADMIN' : 'LOGIN', 1);
}
/**
* Returns default user group for it's type
*
* @return bool|string
* @access protected
*/
protected function getUserTypeGroup()
{
$group_id = false;
$object =& $this->getUserObject();
if ( $object->GetDBField('UserType') == UserType::USER ) {
$group_id = $this->Application->ConfigValue('User_NewGroup');
}
elseif ( $object->GetDBField('UserType') == UserType::ADMIN ) {
$group_id = $this->Application->ConfigValue('User_AdminGroup');
}
$ip_restrictions = $this->getGroupsWithIPRestrictions();
if ( !isset($ip_restrictions[$group_id]) || kUtil::ipMatch($ip_restrictions[$group_id], "\n") ) {
return $group_id;
}
return false;
}
/**
* Returns groups with IP restrictions
*
* @return Array
* @access public
*/
public function getGroupsWithIPRestrictions()
{
static $cache = null;
if ( $this->Application->isDebugMode() ) {
return Array ();
}
if ( !isset($cache) ) {
$sql = 'SELECT IPRestrictions, GroupId
FROM ' . TABLE_PREFIX . 'UserGroups
WHERE COALESCE(IPRestrictions, "") <> ""';
$cache = $this->Conn->GetCol($sql, 'GroupId');
}
return $cache;
}
/**
* Performs user logout
*
*/
function logoutUser()
{
if (!isset($this->event)) {
$this->event = new kEvent('u:OnLogout');
}
$hook_event = new kEvent('u:OnBeforeLogout');
$hook_event->MasterEvent = $this->event;
$this->Application->HandleEvent($hook_event);
$this->_processLoginRedirect();
$user_id = USER_GUEST;
$this->Application->SetVar('u.current_id', $user_id);
/** @var UsersItem $object */
$object = $this->Application->recallObject('u.current', null, Array('skip_autoload' => true));
$object->Load($user_id);
$this->Application->DestroySession();
$this->Application->StoreVar('user_id', $user_id, true);
$this->Application->Session->SetField('PortalUserId', $user_id);
$group_list = $this->Application->ConfigValue('User_GuestGroup') . ',' . $this->Application->ConfigValue('User_LoggedInGroup');
$this->Application->StoreVar('UserGroups', $group_list, true);
$this->Application->Session->SetField('GroupList', $group_list);
if ($this->Application->ConfigValue('UseJSRedirect')) {
$this->event->SetRedirectParam('js_redirect', 1);
}
$this->Application->resetCounters('UserSessions');
$this->Application->Session->SetCookie('remember_login', '', strtotime('-1 hour'));
// don't pass user prefix on logout, since resulting url will have broken "env"
$this->event->SetRedirectParam('pass', MOD_REWRITE ? 'm' : 'all');
$this->_fixNextTemplate();
}
/**
* Returns user id based on given criteria
*
* @param string $username
* @param string $password
* @param string $remember_login_cookie
* @return int
*/
function getUserId($username, $password, $remember_login_cookie)
{
if ( $remember_login_cookie ) {
list ($username, $password) = explode('|', $remember_login_cookie); // 0 - username, 1 - md5(password_hash)
}
$sql = 'SELECT PortalUserId, Password, PasswordHashingMethod
FROM ' . TABLE_PREFIX . 'Users
WHERE ' . (strpos($username, '@') === false ? 'Username' : 'Email') . ' = %1$s';
$user_info = $this->Conn->GetRow(sprintf($sql, $this->Conn->qstr($username)));
if ( $user_info ) {
if ( $remember_login_cookie ) {
return md5($user_info['Password']) == $password ? $user_info['PortalUserId'] : false;
}
else {
/** @var kPasswordFormatter $password_formatter */
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
$hashing_method = $user_info['PasswordHashingMethod'];
if ( $password_formatter->checkPassword($password, $user_info['Password'], $hashing_method) ) {
if ( $hashing_method != PasswordHashingMethod::PHPPASS ) {
$this->_fixUserPassword($user_info['PortalUserId'], $password);
}
return $user_info['PortalUserId'];
}
}
}
return false;
}
/**
* Apply new password hashing to given user's password
*
* @param int $user_id
* @param string $password
* @return void
* @access protected
*/
protected function _fixUserPassword($user_id, $password)
{
/** @var kPasswordFormatter $password_formatter */
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
$fields_hash = Array (
'Password' => $password_formatter->hashPassword($password),
'PasswordHashingMethod' => PasswordHashingMethod::PHPPASS,
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Users', 'PortalUserId = ' . $user_id);
}
/**
* Process all required data and redirect logged-in user
*
* @param string $username
* @param string $password
* @return void
*/
protected function _processLoginRedirect($username = null, $password = null)
{
// set next template
$next_template = $this->Application->GetVar('next_template');
if ( $next_template ) {
$this->event->redirect = $next_template;
}
// process IIS redirect
if ( $this->Application->ConfigValue('UseJSRedirect') ) {
$this->event->SetRedirectParam('js_redirect', 1);
}
// synchronize login
/** @var UsersSyncronizeManager $sync_manager */
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize'));
if ( isset($username) && isset($password) ) {
$sync_manager->performAction('LoginUser', $username, $password);
}
else {
$sync_manager->performAction('LogoutUser');
}
}
/**
* Sets correct interface language after successful login, based on user settings
*
* @return void
* @access protected
*/
protected function _processInterfaceLanguage()
{
if ( defined('IS_INSTALL') && IS_INSTALL ) {
$this->event->SetRedirectParam('m_lang', 1); // data
$this->Application->Session->SetField('Language', 1); // interface
return;
}
$language_field = $this->Application->isAdmin ? 'AdminLanguage' : 'FrontLanguage';
$primary_language_field = $this->Application->isAdmin ? 'AdminInterfaceLang' : 'PrimaryLang';
$is_root = $this->Application->RecallVar('user_id') == USER_ROOT;
$object =& $this->getUserObject();
$user_language_id = $is_root ? $this->Application->RecallPersistentVar($language_field) : $object->GetDBField($language_field);
$sql = 'SELECT LanguageId, IF(LanguageId = ' . (int)$user_language_id . ', 2, ' . $primary_language_field . ') AS SortKey
FROM ' . TABLE_PREFIX . 'Languages
WHERE Enabled = 1
HAVING SortKey <> 0
ORDER BY SortKey DESC';
$language_info = $this->Conn->GetRow($sql);
$language_id = $language_info && $language_info['LanguageId'] ? $language_info['LanguageId'] : $user_language_id;
if ( $user_language_id != $language_id ) {
// first login OR language was deleted or disabled
if ( $is_root ) {
$this->Application->StorePersistentVar($language_field, $language_id);
}
else {
$object->SetDBField($language_field, $language_id);
$object->Update();
}
}
// set language for Admin Console & Front-End with disabled Mod-Rewrite
$this->event->SetRedirectParam('m_lang', $language_id); // data
$this->Application->Session->SetField('Language', $language_id); // interface
}
/**
* Injects redirect params into next template, which doesn't happen if next template starts with "external:"
*
* @return void
* @access protected
*/
protected function _fixNextTemplate()
{
if ( !MOD_REWRITE || !is_object($this->event) ) {
return;
}
// solve problem, when template is "true" instead of actual template name
$template = is_string($this->event->redirect) ? $this->event->redirect : '';
$url = $this->Application->HREF($template, '', $this->event->getRedirectParams(), $this->event->redirectScript);
$vars = $this->Application->parseRewriteUrl($url, 'pass');
unset($vars['login'], $vars['logout']);
// merge back url params, because they were ignored if this was "external:" url
$vars = array_merge($vars, $this->getRedirectParams($vars['pass'], 'pass'));
if ( $template != 'index' ) {
// The 'index.html' becomes '', which in turn leads to current page instead of 'index.html'.
$template = $vars['t'];
}
unset($vars['is_virtual'], $vars['t']);
$this->event->redirect = $template;
$this->event->setRedirectParams($vars, false);
}
/**
* Returns current event redirect params with given $prefixes injected into 'pass'.
*
* @param array $prefixes List of prefixes to inject.
* @param string $pass_name Name of array key in redirect params, containing comma-separated prefixes list.
*
* @return string
* @access protected
*/
protected function getRedirectParams($prefixes, $pass_name = 'passed')
{
$redirect_params = $this->event->getRedirectParams();
if ( isset($redirect_params[$pass_name]) ) {
$redirect_prefixes = explode(',', $redirect_params[$pass_name]);
$prefixes = array_unique(array_merge($prefixes, $redirect_prefixes));
}
$redirect_params[$pass_name] = implode(',', $prefixes);
return $redirect_params;
}
/**
* Checks that user is allowed to use super admin mode
*
* @return bool
*/
function verifySuperAdmin()
{
$sa_mode = kUtil::ipMatch(defined('SA_IP') ? SA_IP : '');
return $sa_mode || $this->Application->isDebugMode();
}
/**
* Returns user object, used during login processing
*
* @return UsersItem
* @access public
*/
public function &getUserObject()
{
$prefix_special = $this->Application->isAdmin ? 'u.current' : 'u'; // "u" used on front not to change theme
/** @var UsersItem $object */
$object = $this->Application->recallObject($prefix_special, null, Array('skip_autoload' => true));
return $object;
}
/**
* Checks, if given user fields matches at least one of defined ban rules
*
* @param kDBItem $object
* @return bool
*/
function checkBanRules(&$object)
{
$table = $this->Application->getUnitOption('ban-rule', 'TableName');
if (!$this->Conn->TableFound($table)) {
// when ban table not found -> assume user is ok by default
return true;
}
$sql = 'SELECT *
FROM ' . $table . '
WHERE ItemType = 6 AND Status = ' . STATUS_ACTIVE . '
ORDER BY Priority DESC';
$rules = $this->Conn->Query($sql);
$found = false;
foreach ($rules as $rule) {
$field = $rule['ItemField'];
$this_value = mb_strtolower( $object->GetDBField($field) );
$test_value = mb_strtolower( $rule['ItemValue'] );
switch ( $rule['ItemVerb'] ) {
case 1: // is
if ($this_value == $test_value) {
$found = true;
}
break;
case 2: // is not
if ($this_value != $test_value) {
$found = true;
}
break;
case 3: // contains
if ( strstr($this_value, $test_value) ) {
$found = true;
}
break;
case 4: // not contains
if ( !strstr($this_value, $test_value) ) {
$found = true;
}
break;
case 7: // exists
if ( strlen($this_value) > 0 ) {
$found = true;
}
break;
case 8: // unique
if ( $this->_checkValueExist($field, $this_value) ) {
$found = true;
}
break;
}
if ( $found ) {
// check ban rules, until one of them matches
if ( $rule['RuleType'] ) {
// invert rule type
$found = false;
}
break;
}
}
return !$found;
}
/**
* Checks if value is unique in Users table against the specified field
*
* @param string $field
* @param string $value
* @return string
*/
function _checkValueExist($field, $value)
{
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('u', 'TableName') . '
WHERE '. $field .' = ' . $this->Conn->qstr($value);
return $this->Conn->GetOne($sql);
}
public function validateUserCode($user_code, $code_type, $expiration_timeout = null)
{
$expiration_timeouts = Array (
'forgot_password' => 'config:Users_AllowReset',
'activation' => 'config:UserEmailActivationTimeout',
'verify_email' => 'config:Users_AllowReset',
'custom' => '',
);
if ( !$user_code ) {
return 'code_is_not_valid';
}
$sql = 'SELECT PwRequestTime, PortalUserId
FROM ' . TABLE_PREFIX . 'Users
WHERE PwResetConfirm = ' . $this->Conn->qstr( trim($user_code) );
$user_info = $this->Conn->GetRow($sql);
if ( $user_info === false ) {
return 'code_is_not_valid';
}
$expiration_timeout = isset($expiration_timeout) ? $expiration_timeout : $expiration_timeouts[$code_type];
if ( preg_match('/^config:(.*)$/', $expiration_timeout, $regs) ) {
$expiration_timeout = $this->Application->ConfigValue( $regs[1] );
}
if ( $expiration_timeout && $user_info['PwRequestTime'] < strtotime('-' . $expiration_timeout . ' minutes') ) {
return 'code_expired';
}
return $user_info['PortalUserId'];
}
/**
* Restores user's email, returns error label, if error occurred
*
* @param string $hash
* @return string
* @access public
*/
public function restoreEmail($hash)
{
if ( !preg_match('/^[a-f0-9]{32}$/', $hash) ) {
return 'invalid_hash';
}
$sql = 'SELECT PortalUserId, PrevEmails
FROM ' . TABLE_PREFIX . 'Users
WHERE PrevEmails LIKE ' . $this->Conn->qstr('%' . $hash . '%');
$user_info = $this->Conn->GetRow($sql);
if ( $user_info === false ) {
return 'invalid_hash';
}
$prev_emails = $user_info['PrevEmails'];
$prev_emails = $prev_emails ? unserialize($prev_emails) : Array ();
if ( !isset($prev_emails[$hash]) ) {
return 'invalid_hash';
}
$email_to_restore = $prev_emails[$hash];
unset($prev_emails[$hash]);
/** @var UsersItem $object */
$object = $this->Application->recallObject('u.email-restore', null, Array ('skip_autoload' => true));
$object->Load($user_info['PortalUserId']);
$object->SetDBField('PrevEmails', serialize($prev_emails));
$object->SetDBField('Email', $email_to_restore);
$object->SetDBField('EmailVerified', 1);
return $object->Update() ? '' : 'restore_impossible';
}
/**
* Returns user's primary group.
*
* @param integer $user_id User ID.
*
* @return integer
*/
public function getPrimaryGroup($user_id)
{
if ( $user_id <= 0 ) {
return $this->Application->ConfigValue('User_LoggedInGroup');
}
$cache_key = 'user' . $user_id . '_primary_group[%UIDSerial:' . $user_id . '%]';
$cache_value = $this->Application->getCache($cache_key);
if ( $cache_value === false ) {
$sql = 'SELECT PrimaryGroupId
FROM ' . TABLE_PREFIX . 'Users
WHERE PortalUserId = ' . $user_id;
$cache_value = $this->Conn->GetOne($sql);
$this->Application->setCache($cache_key, $cache_value);
}
return $cache_value;
}
}

Event Timeline