From f713eb0d153eadb963f30e08166c852dc198a691 Mon Sep 17 00:00:00 2001 From: secretr Date: Thu, 13 May 2010 15:47:31 +0000 Subject: [PATCH] EONE-62 (New Feature): login/init session working now; added user preferences management; new experimental feature 'login as'; need more work regarding DB related operations and security --- class2.php | 13 +- e107_handlers/e107_class.php | 55 +++- e107_handlers/login.php | 37 ++- e107_handlers/model_class.php | 4 +- e107_handlers/pref_class.php | 61 ++-- e107_handlers/user_handler.php | 148 ++++----- e107_handlers/user_model.php | 572 ++++++++++++++++++++++++++++++--- 7 files changed, 705 insertions(+), 185 deletions(-) diff --git a/class2.php b/class2.php index 1aff09d5a..d5ba41fdf 100644 --- a/class2.php +++ b/class2.php @@ -2,16 +2,14 @@ /* * e107 website system * -* Copyright (C) 2008-2009 e107 Inc (e107.org) +* Copyright (C) 2008-2010 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * General purpose file * -* $Source: /cvs_backup/e107_0.8/class2.php,v $ -* $Revision$ -* $Date$ -* $Author$ +* $URL$ +* $Id$ * */ // @@ -1556,10 +1554,11 @@ function init_session() */ - global $pref, $user_pref, $currentUser, $e107, $_E107; + global $pref, $user_pref, $currentUser, $_E107; $sql = e107::getDb(); $tp = e107::getParser(); + $e107 = e107::getInstance(); $eArrayStorage = e107::getArrayStorage(); @@ -1596,7 +1595,7 @@ function init_session() { list($uid, $upw)=(isset($_COOKIE[e_COOKIE]) && $_COOKIE[e_COOKIE] ? explode(".", $_COOKIE[e_COOKIE]) : explode(".", $_SESSION[e_COOKIE])); } - else + else // FIXME - this will never happen - see above { list($uid, $upw)= explode('.', $cli_log); } diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php index 0a6f83dd4..94915196d 100644 --- a/e107_handlers/e107_class.php +++ b/e107_handlers/e107_class.php @@ -51,6 +51,14 @@ class e107 public $site_theme; + /** + * Contains reference to global $_E107 array + * Assignment is done inside prepare_request() method + * + * @var array + */ + protected $_E107 = array(); + /** * @var string Current request type (http or https) */ @@ -175,6 +183,7 @@ class e107 'sitelinks' => '{e_HANDLER}sitelinks_class.php', 'themeHandler' => '{e_HANDLER}theme_handler.php', 'user_class' => '{e_HANDLER}userclass_class.php', + 'userlogin' => '{e_HANDLER}login.php', 'xmlClass' => '{e_HANDLER}xml_class.php', ); @@ -477,6 +486,31 @@ class e107 return (isset($this->e107_dirs[$key]) ? $this->e107_dirs[$key] : ''); } + /** + * Get value from $_E107 config array + * Note: will always return false if called before prepare_request() method! + * + * @param string $key + * @return boolean + */ + public static function getE107($key) + { + $self = self::getInstance(); + return (isset($self->_E107[$key]) && $self->_E107[$key] ? true : false); + } + + /** + * Convenient proxy to $_E107 getter - check if + * the system is currently running in cli mode + * Note: will always return false if called before prepare_request() method! + * + * @return boolean + */ + public static function isCli() + { + return self::getE107('cli'); + } + /** * Get mysql config var (e107_config.php) * Replaces all $mySQL(*) globals @@ -1099,7 +1133,7 @@ class e107 { return self::getUser(); } - $user = self::getRegistry('targets/core/user/'.$user_id); + $user = self::getRegistry('core/e107/user/'.$user_id); if(null === $user) { $user = self::getObject('e_system_user'); @@ -1115,7 +1149,13 @@ class e107 */ public static function getUser() { - return self::getSingleton('e_user', true, 'targets/core/current_user'); + $user = self::getRegistry('core/e107/current_user'); + if(null === $user) + { + $user = self::getObject('e_user'); + self::setRegistry('core/e107/current_user', $user); + } + return $user; } /** @@ -1731,8 +1771,11 @@ class e107 } } + // we can now start use $e107->_E107 + if(isset($GLOBALS['_E107']) && is_array($GLOBALS['_E107'])) $this->_E107 = & $GLOBALS['_E107']; + // remove ajax_used=1 from query string to avoid SELF problems, ajax should always be detected via e_AJAX_REQUEST constant - $_SERVER['QUERY_STRING'] = str_replace(array('ajax_used=1', '&&'), array('', '&'), $_SERVER['QUERY_STRING']); + $_SERVER['QUERY_STRING'] = trim(str_replace(array('ajax_used=1', '&&'), array('', '&'), $_SERVER['QUERY_STRING']), '&'); // e107 uses relative url's, which are broken by "pretty" URL's. So for now we don't support / after .php if(($pos = strpos($_SERVER['PHP_SELF'], '.php/')) !== false) // redirect bad URLs to the correct one. @@ -1853,8 +1896,6 @@ class e107 */ public function set_paths() { - global $_E107; - // ssl_enabled pref not needed anymore, scheme is auto-detected $this->HTTP_SCHEME = 'http'; if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') @@ -1865,7 +1906,7 @@ class e107 $path = ""; $i = 0; // FIXME - Again, what if someone moves handlers under the webroot? - if(!isset($_E107['cli'])) + if(!self::isCli()) { while (!file_exists("{$path}class2.php")) { @@ -1904,7 +1945,7 @@ class e107 } define('e_ROOT', $e_ROOT); // Specified format gives trailing slash already (at least on Windows) - $this->relative_base_path = (!isset($_E107['cli'])) ? $path : e_ROOT; + $this->relative_base_path = (!self::isCli()) ? $path : e_ROOT; $this->http_path = "http://{$_SERVER['HTTP_HOST']}{$this->server_path}"; $this->https_path = "https://{$_SERVER['HTTP_HOST']}{$this->server_path}"; $this->file_path = $path; diff --git a/e107_handlers/login.php b/e107_handlers/login.php index d215eef7b..e1d151310 100644 --- a/e107_handlers/login.php +++ b/e107_handlers/login.php @@ -3,16 +3,14 @@ /* * e107 website system * - * Copyright (C) 2008-2009 e107 Inc (e107.org) + * Copyright (C) 2008-2010 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * e107 Main * - * $Source: /cvs_backup/e107_0.8/e107_handlers/login.php,v $ - * $Revision$ - * $Date$ - * $Author$ + * $Id$ + * $URL$ */ @@ -61,7 +59,7 @@ class userlogin ' @param string $response - response string returned by CHAP login (instead of password) # @return boolean - FALSE on login fail, TRUE on login successful */ - public function __construct($username, $userpass, $autologin, $response = '') + public function __construct($username, $userpass, $autologin, $response = '', $noredirect = false) { global $pref, $e_event, $_E107; @@ -72,7 +70,7 @@ class userlogin { return FALSE; } - + $tp = e107::getParser(); $sql = e107::getDb(); @@ -102,7 +100,7 @@ class userlogin { if (varset($pref['auth_badpassword'], TRUE) || ($this->checkUserPassword($userpass, $response, $forceLogin) === TRUE)) { - $result = LOGIN_CONTINUE; // Valid User exists in local DB + $result = LOGIN_CONTINUE; // Valid User exists in local DB } } } @@ -248,6 +246,8 @@ class userlogin } } + if($noredirect) return; + $redir = e_SELF; if (e_QUERY) $redir .= '?'.str_replace('&','&',e_QUERY); if (isset($pref['frontpage_force']) && is_array($pref['frontpage_force'])) @@ -267,19 +267,24 @@ class userlogin } } } - - $redirPrev = e107::getRedirect()->getPreviousUrl(); - + + $redirPrev = e107::getRedirect()->getPreviousUrl(); + if($redirPrev) - { - e107::getRedirect()->redirect($redirPrev); + { + e107::getRedirect()->redirect($redirPrev); } - e107::getRedirect()->redirect($redir); + e107::getRedirect()->redirect($redir); exit(); } + public function getUserData() + { + return $this->userData; + } + /** * Look up a user in the e107 database, according to the options set (for login name/email address) * Note: PASSWORD IS NOT VERIFIED BY THIS ROUTINE @@ -312,7 +317,7 @@ class userlogin { // Invalid user return $this->invalidLogin($username,LOGIN_BAD_USER); } - + // User is in DB here $this->userData = $this->e107->sql -> db_Fetch(MYSQL_ASSOC); // Get user info $this->userData['user_perms'] = trim($this->userData['user_perms']); @@ -432,7 +437,7 @@ class userlogin break; case LOGIN_NOT_ACTIVATED : $srch = array("[","]"); - $repl = array("",""); + $repl = array("",""); define("LOGINMESSAGE", str_replace($srch,$repl,LAN_LOGIN_22)."

"); $this->logNote('LAN_ROLL_LOG_05', $username); $this->genNote($username, LAN_LOGIN_27); diff --git a/e107_handlers/model_class.php b/e107_handlers/model_class.php index 7cb9b71d0..fffab76eb 100644 --- a/e107_handlers/model_class.php +++ b/e107_handlers/model_class.php @@ -35,7 +35,7 @@ class e_model protected $_data = array(); /** - * Data structure (types) array, required for {@link e_admin_model::sanitize()} method, + * Data structure (types) array, required for {@link e_front_model::sanitize()} method, * it also serves as a map (find data) for building DB queries, * copy/sanitize posted data to object data, etc. * @@ -1382,7 +1382,7 @@ class e_front_model extends e_model * Predefined data fields types, passed to DB handler * * @param array $field_types - * @return e_model + * @return e_front_model */ public function setDbTypes($field_types) { diff --git a/e107_handlers/pref_class.php b/e107_handlers/pref_class.php index c245def17..fccc882ba 100644 --- a/e107_handlers/pref_class.php +++ b/e107_handlers/pref_class.php @@ -2,16 +2,14 @@ /* * e107 website system * - * Copyright (C) 2008-2009 e107 Inc (e107.org) + * Copyright (C) 2008-2010 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * e107 Preference Handler * - * $Source: /cvs_backup/e107_0.8/e107_handlers/pref_class.php,v $ - * $Revision$ - * $Date$ - * $Author$ + * $URL$ + * $Id$ */ if (!defined('e107_INIT')) { exit; } @@ -23,11 +21,11 @@ require_once(e_HANDLER.'model_class.php'); * * @package e107 * @category e107_handlers - * @version 1.0 + * @version $Id$ * @author SecretR * @copyright Copyright (c) 2009, e107 Inc. */ -class e_pref extends e_admin_model +class e_pref extends e_front_model { /** * Preference ID - DB row value @@ -504,7 +502,7 @@ class e_pref extends e_admin_model e107::getMessage()->addInfo('Settings not saved as no changes were made.', 'default', $session_messages); return 0; } - + $admin_log = e107::getAdminLog(); //Save to DB @@ -538,7 +536,7 @@ class e_pref extends e_admin_model // auto admin log if(is_array($old)) // fix install problems - no old prefs available { - $new = $this->getPref(); + $new = $this->getPref(); $admin_log->logArrayDiffs($new, $old, 'PREFS_02', false); unset($new, $old); } @@ -682,31 +680,16 @@ class e_pref extends e_admin_model /** * Override */ - public function dbInsert() + public function delete() { } /** * Override */ - public function dbUpdate() + protected function dbUpdate() { } - - /** - * Override - */ - public function dbReplace() - { - } - - /** - * Override - */ - public function dbDelete() - { - } - } /** @@ -861,6 +844,22 @@ class e_plugin_pref extends e_pref { return $this->plugin_id; } + + /** + * Delete plugin preferences + * @see e107_handlers/e_pref#delete() + * @return boolean + */ + public function delete() + { + $ret = false; + if($this->plugin_id) + { + $ret = e107::getDb($this->plugin_id)->db_Delete('core', "e107_name='{$this->plugin_id}'"); + $this->destroy(); + } + return $ret; + } } /** @@ -1037,13 +1036,13 @@ class prefs * * all pref sets other than menu_pref get toDB() */ - function setArray($name = '', $table = 'core', $uid = USERID) + function setArray($name = '', $table = 'core', $uid = USERID) { $tp = e107::getParser(); - if (!strlen($name)) + if (!strlen($name)) { - switch ($table) + switch ($table) { case 'core': $name = 'pref'; @@ -1055,9 +1054,9 @@ class prefs } global $$name; - if ($name != 'menu_pref') + if ($name != 'menu_pref') { - foreach($$name as $key => $prefvalue) + foreach($$name as $key => $prefvalue) { $$name[$key] = $tp->toDB($prefvalue); } diff --git a/e107_handlers/user_handler.php b/e107_handlers/user_handler.php index 80dcb66cc..e78abe1dd 100644 --- a/e107_handlers/user_handler.php +++ b/e107_handlers/user_handler.php @@ -2,22 +2,20 @@ /* * e107 website system * - * Copyright (C) 2008-2009 e107 Inc (e107.org) + * Copyright (C) 2008-2010 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * Handler - user-related functions * - * $Source: /cvs_backup/e107_0.8/e107_handlers/user_handler.php,v $ - * $Revision$ - * $Date$ - * $Author$ + * $URL$ + * $Id$ * */ /** - * + * * @package e107 * @subpackage e107_handlers * @version $Id$; @@ -181,8 +179,8 @@ class UserHandler * @param string $password - plaintext password as entered by user * @param string $login_name - string used to log in (could actually be email address) * @param string $stored_hash - required value for password to match - * - * @return PASSWORD_INVALID|PASSWORD_VALID|string + * + * @return PASSWORD_INVALID|PASSWORD_VALID|string * PASSWORD_INVALID if no match * PASSWORD_VALID if valid password * Return a new hash to store if valid password but non-preferred encoding @@ -445,25 +443,27 @@ class UserHandler * @param array $lode - user information from DB - 'user_id' and 'user_password' required * @param bool $autologin - TRUE if the 'Remember Me' box ticked * - * @return none + * @return void */ public function makeUserCookie($lode,$autologin = FALSE) { global $pref; $cookieval = $lode['user_id'].'.'.md5($lode['user_password']); // (Use extra md5 on cookie value to obscure hashed value for password) - if ($pref['user_tracking'] == 'session') + if (e107::getPref('user_tracking') == 'session') { - $_SESSION[$pref['cookie_name']] = $cookieval; + $_SESSION[e107::getPref('cookie_name')] = $cookieval; } else { if ($autologin == 1) { // Cookie valid for up to 30 days - cookie($pref['cookie_name'], $cookieval, (time() + 3600 * 24 * 30)); + cookie(e107::getPref('cookie_name'), $cookieval, (time() + 3600 * 24 * 30)); + $_COOKIE[e107::getPref('cookie_name')] = $cookieval; // make it available to the global scope before the page is reloaded } else { - cookie($pref['cookie_name'], $cookieval); + cookie(e107::getPref('cookie_name'), $cookieval); + $_COOKIE[e107::getPref('cookie_name')] = $cookieval; // make it available to the global scope before the page is reloaded } } } @@ -833,7 +833,7 @@ e107::includeLan(e_LANGUAGEDIR.e_LANGUAGE."/admin/lan_administrator.php"); class e_userperms { protected $core_perms = array( - + "1"=> ADMSLAN_19, "2"=> ADMSLAN_20, "3"=> ADMSLAN_21, @@ -868,39 +868,39 @@ class e_userperms "N"=> ADMSLAN_47, // "Z"=> ADMSLAN_62, ); - + protected $plugin_perms = array(); - + protected $language_perms = array(); - + protected $main_perms = array(); - + protected $permSectionDiz = array( 'core' => ADMSLAN_74, 'plugin' => ADLAN_CL_7, 'language' => ADLAN_132, 'main' => ADMSLAN_58 ); - - + + function __construct() { - - + + $sql = e107::getDb('sql2'); $tp = e107::getParser(); - - + + $sql->db_Select("plugin", "*", "plugin_installflag='1'"); while ($row2 = $sql->db_Fetch()) { $this->plugin_perms[("P".$row2['plugin_id'])] = LAN_PLUGIN." - ".$tp->toHTML($row2['plugin_name'], FALSE, 'RAWTEXT,defs'); - } - + } + asort($this->plugin_perms); - + $this->plugin_perms = array("Z"=>ADMSLAN_62) + $this->plugin_perms; - + if(e107::getConfig()->getPref('multilanguage')) { $lanlist = explode(",",e_LANLIST); @@ -910,20 +910,20 @@ class e_userperms $this->language_perms[$langs] = $langs; } } - + if(getperms('0')) { $this->main_perms = array('0' => ADMSLAN_58); } - + } - + function renderSectionDiz($key) { - return $this->permSectionDiz[$key]; + return $this->permSectionDiz[$key]; } - - + + function getPermList($type='all') { if($type == 'core') @@ -942,34 +942,34 @@ class e_userperms { return $this->main_perms; } - + if($type == 'grouped') { $ret = array(); $ret['core'] = $this->core_perms; $ret['plugin'] = $this->plugin_perms; - + if(vartrue($this->language_perms)) { $ret['language'] = $this->language_perms; } - + if(vartrue($this->main_perms)) { $ret['main'] = $this->main_perms; } - + return $ret; - + } - + return array_merge($this->core_perms,$this->plugin_perms,$this->language_perms,$this->main_perms); } - + function checkb($arg, $perms, $label='') { $frm = e107::getForm(); - + $par = "
"; $par .= $frm->checkbox('perms[]', $arg, getperms($arg, $perms)); if ($label) @@ -977,44 +977,44 @@ class e_userperms $par .= $frm->label($label,'perms[]', $arg); } $par .= "
\n"; - + return $par; } - + function renderPerms($perms,$uniqueID='') { $tmp = explode(".",$perms); $permdiz = $this->getPermList(); $ptext = array(); - + foreach($tmp as $p) { $ptext[] = $permdiz[$p]; - } - + } + $id = "id_".$uniqueID; - - + + $text = "
{$perms}
"; - + if(varset($ptext)) { $text .= "
"; } - + /* $text = "{$perms}"; - + if(varset($ptext)) { $text .= "
"; } - */ + */ return $text; } - + /** - * Render edit admin perms form. + * Render edit admin perms form. * * @param array $row [optional] containing $row['user_id'], $row['user_name'], $row['user_perms']; * @return void @@ -1028,12 +1028,12 @@ class e_userperms $ns = e107::getRender(); $sql = e107::getDb(); $frm = e107::getForm(); - - + + $a_id = $row['user_id']; $ad_name = $row['user_name']; $a_perms = $row['user_perms']; - + $text = "
@@ -1054,21 +1054,21 @@ class e_userperms ".ADMSLAN_18." - + "; - + $groupedList = $prm->getPermList('grouped'); - + foreach($groupedList as $section=>$list) { - $text .= "\t\t

".$prm->renderSectionDiz($section)."

"; //XXX Lan - General + $text .= "\t\t

".$prm->renderSectionDiz($section)."

"; //XXX Lan - General foreach($list as $key=>$diz) { - $text .= $prm->checkb($key, $a_perms, $diz); + $text .= $prm->checkb($key, $a_perms, $diz); } $text .= "
"; } - + $text .= "
".$frm->admin_button('check_all', 'jstarget:perms', 'action', LAN_CHECKALL)." ".$frm->admin_button('uncheck_all', 'jstarget:perms', 'action', LAN_UNCHECKALL)." @@ -1085,37 +1085,37 @@ class e_userperms
"; - + $ns->tablerender(ADMSLAN_52, $text); } - + /** * Update user (admin) permissions. * NOTE: exit if $uid is not an integer or is 0. * * @param integer $uid * @param array $permArray eg. array('A', 'K', '1'); - * @return void + * @return void */ function updatePerms($uid, $permArray) { global $admin_log; - + $sql = e107::getDb(); $tp = e107::getParser(); - + $modID = intval($uid); if ($modID == 0) { exit(); } - + $sql->db_Select("user", "*", "user_id=".$modID); $row = $sql->db_Fetch(); $a_name = $row['user_name']; - + $perm = ""; - + foreach($permArray as $value) { $value = $tp->toDB($value); @@ -1124,13 +1124,13 @@ class e_userperms if (!getperms('0')) { $value = ""; break; } $perm = "0"; break; } - + if ($value) { $perm .= $value."."; } } - + admin_update($sql->db_Update("user", "user_perms='{$perm}' WHERE user_id='{$modID}' "), 'update', sprintf(ADMSLAN_2, $tp->toDB($_POST['ad_name'])), false, false); $logMsg = str_replace(array('--ID--', '--NAME--'),array($modID, $a_name),ADMSLAN_72).$perm; $admin_log->log_event('ADMIN_01',$logMsg,E_LOG_INFORMATIVE,''); diff --git a/e107_handlers/user_model.php b/e107_handlers/user_model.php index bc9e01d52..04de61c80 100644 --- a/e107_handlers/user_model.php +++ b/e107_handlers/user_model.php @@ -69,7 +69,7 @@ class e_user_model extends e_front_model protected $_validation_rules = array( 'user_name' => array('string', '1', 'LAN_USER_01', 'LAN_USER_HELP_01'), // TODO - regex 'user_loginname' => array('string', '1', 'LAN_USER_02', 'LAN_USER_HELP_02'), // TODO - regex - 'user_password' => array('string', '5', 'LAN_USER_05', 'LAN_USER_HELP_05'), // TODO - pref - modify it somewhere below + 'user_password' => array('compare', '5', 'LAN_USER_05', 'LAN_USER_HELP_05'), // TODO - pref - modify it somewhere below - prepare_rules()? 'user_email' => array('email', '', 'LAN_USER_08', 'LAN_USER_HELP_08'), ); @@ -113,6 +113,12 @@ class e_user_model extends e_front_model */ protected $_extended_structure = null; + /** + * User preferences model + * @var e_user_pref + */ + protected $_user_config = null; + /** * User model of current editor * @var e_user_model @@ -176,7 +182,7 @@ class e_user_model extends e_front_model public function hasEditor() { - return null !== $this->_editor; + return (null !== $this->_editor); } final protected function _setClassList($uid = '') @@ -247,24 +253,74 @@ class e_user_model extends e_front_model * * @param string$field * @param string $default + * @param boolean $short if true, 'user_' prefix will be added to field name * @return mixed */ - public function getValue($field, $default = '') + public function getValue($field, $default = '', $short = true) { - $field = 'user_'.$field; + if($short) $field = 'user_'.$field; return $this->get($field, $default); } /** - * Set User value + * Set User value - only when writable * @param string $field * @param mixed $value + * @param boolean $short if true, 'user_' prefix will be added to field name + * @return e_user_model + */ + public function setValue($field, $value, $short = true) + { + if($short) $field = 'user_'.$field; + if($this->isWritable($field)) $this->set($field, $value, true); + return $this; + } + + /** + * Get user preference + * @param string $pref_name + * @param mixed $default + * @return mixed + */ + public function getPref($pref_name = null, $default = null) + { + if(null === $pref_name) return $this->getConfig()->getData(); + return $this->getConfig()->get($pref_name, $default); + } + + /** + * Set user preference + * @param string $pref_name + * @param mixed $value * @return e_user_model */ - public function setValue($field, $value) + public function setPref($pref_name, $value = null) { - $field = 'user_'.$field; - $this->set($field, $value, true); + $this->getConfig()->set($pref_name, $value); + return $this; + } + + /** + * Get user preference (advanced - slower) + * @param string $pref_path + * @param mixed $default + * @param integer $index if number, value will be exploded by "\n" and corresponding index will be returned + * @return mixed + */ + public function findPref($pref_path = null, $default = null, $index = null) + { + return $this->getConfig()->getData($pref_path, $default, $index); + } + + /** + * Set user preference (advanced - slower) + * @param string $pref_path + * @param mixed $value + * @return e_user_model + */ + public function setPrefData($pref_path, $value = null) + { + $this->getConfig()->setData($pref_path, $value = null); return $this; } @@ -319,6 +375,32 @@ class e_user_model extends e_front_model return $this; } + /** + * Get user config model + * + * @return e_user_pref + */ + public function getConfig() + { + if (null === $this->_user_config) + { + $this->_user_config = new e_user_pref($this); + } + return $this->_user_config; + } + + /** + * Set user config model + * + * @param e_user_pref $user_config + * @return e_user_model + */ + public function setConfig(e_user_pref $user_config) + { + $this->_user_config = $user_config; + return $this; + } + /** * Get current user editor model * @return e_user_model @@ -345,9 +427,11 @@ class e_user_model extends e_front_model */ public function isWritable($field) { - if (!is_string($field)) - return true; - return !in_array($field, array($this->getFieldIdName(), 'user_admin', 'user_perms')); + $perm = false; + $editor = $this->getEditor(); + if($this->getId() === $editor->getId() || $editor->isMainAdmin() || $editor->checkAdminPerms('4')) + $perm = true; + return ($perm && !in_array($field, array($this->getFieldIdName(), 'user_admin', 'user_perms', 'user_prefs'))); } /** @@ -357,7 +441,7 @@ class e_user_model extends e_front_model */ protected function setAsTarget() { - e107::setRegistry('targets/core/user/'.$this->getId(), $this); + e107::setRegistry('core/e107/user/'.$this->getId(), $this); return $this; } @@ -368,7 +452,7 @@ class e_user_model extends e_front_model */ protected function clearTarget() { - e107::setRegistry('targets/core/user'.$this->getId(), null); + e107::setRegistry('core/e107/user'.$this->getId(), null); return $this; } @@ -386,6 +470,39 @@ class e_user_model extends e_front_model } } + /** + * Additional security while applying posted + * data to user model + * @return e_user_model + */ + public function mergePostedData() + { + $posted = $this->getPostedData(); + foreach ($posted as $key => $value) + { + if(!$this->isWritable($key)) + { + $this->removePosted($key); + continue; + } + $this->_modifyPostedData($key, $value); + } + parent::mergePostedData(true, true, true); + return $this; + } + + protected function _modifyPostedData($key, $value) + { + // TODO - add more here + switch ($key) + { + case 'password1': + // compare validation rule + $this->setPosted('user_password', array($value, $this->getPosted('password2'))); + break; + } + } + /** * Send model data to DB */ @@ -396,7 +513,10 @@ class e_user_model extends e_front_model return false; // TODO - message, admin log } - // TODO - do the save manual in this order: validate() on user model, save() on extended fields, save() on user model + // sync user prefs + $this->getConfig()->apply(); + + // TODO - do the save manually in this order: validate() on user model, save() on extended fields, save() on user model $ret = parent::save(true, $force, $session); if(false !== $ret && null !== $this->_extended_model) // don't load extended fields if not already used { @@ -468,15 +588,27 @@ class e_system_user extends e_user_model } /** - * Current system user - additional data protection is required + * Current system user * @author SecretR */ class e_user extends e_user_model { + private $_session_data = null; + private $_session_key = null; + private $_session_type = null; + private $_session_error = false; + + private $_parent_id = false; + private $_parent_data = array(); + private $_parent_extmodel = null; + private $_parent_extstruct = null; + private $_parent_config = null; + public function __construct() { - // reference to self - $this->load()->setEditor($this); + $this->setSessionData() // retrieve data from current session + ->load() // load current user from DB + ->setEditor($this); // reference to self } /** @@ -489,56 +621,270 @@ class e_user extends e_user_model return true; } - // TODO login by name/password, load, set cookie/session data + /** + * Get parent user ID - present if main admin is browsing + * front-end logged in as another user account + * + * @return integer or false if not present + */ + final public function getParentId() + { + return $this->_parent_id; + } + + /** + * User login + * @param string $uname + * @param string $upass_plain + * @param boolean $uauto + * @param string $uchallange + * @return boolean success + */ final public function login($uname, $upass_plain, $uauto = false, $uchallange = false) { - // FIXME - rewrite userlogin - clean up redirects and - //$userlogin = new userlogin($uname, $upass_plain, $uauto, $uchallange); - // if($userlogin->getId()) $this->load() --> use the previously set user COOKIE/SESSION data + if($this->isUser()) return false; + + $userlogin = new userlogin($uname, $upass_plain, $uauto, $uchallange, true); + $this->setSessionData(true) + ->setData($userlogin->getUserData()); + return $this->isUser(); } + final public function loginAs($user_id) + { + // TODO - set session data required for loadAs() + } + /** * - * @return unknown_type + * @return e_user */ - protected function initConstants() + protected function _initConstants() { //FIXME - BC - constants from init_session() should be defined here - //init_session(); // the old way + // [SecretR] Not sure we should do this here, it's too restricting - constants can be + // defined once, we need the freedom to do it multiple times - e.g. load() executed in constructor than login(), loginAs() etc. + // called by a controller + // We should switch to e.g. isAdmin() instead of ADMIN constant check + return $this; } /** - * TODO destroy cookie/session data, self destroy - * @return void + * Destroy cookie/session data, self destroy + * @return e_user */ final public function logout() { - // FIXME - destoy cookie/session data first - $this->_data = array(); - if (null !== $this->_extended_model) + $this->logoutAs() + ->_destroySession(); + + parent::destroy(); + if(session_id()) session_destroy(); + + e107::setRegistry('core/e107/current_user', null); + return $this; + } + + /** + * Destroy cookie/session/model data for current user, resurrect parent user + * @return e_user + */ + final public function logoutAs() + { + if($this->getParentId()) { - $this->_extended_model->destroy(); + // load parent user data + $this->_extended_model = $this->_parent_extmodel; + $this->_extended_structure = $this->_parent_extstruct; + $this->_user_config = $this->_parent_config; + $this->setData($this->_parent_model->getData()); + + // cleanup + $this->_destroyAsSession(); + $this->_parent_id = false; + $this->_parent_model = $this->_parent_extstruct = $this->_parent_extmodel = $this->_parent_config = null; } - e107::setRegistry('targets/core/current_user', null); + return $this; } /** * TODO load user data by cookie/session data * @return e_user */ - final public function load($force = false) + final public function load($force = false, $denyAs = false) { // init_session() should come here // $this->initConstants(); - called after data is loaded - // FIXME - temporary here, for testing only!!! + if(!$force && $this->getId()) return $this; + + // always run cli as main admin + if(e107::isCli()) + { + $this->_load(1, $force); + $this->_initConstants(); + return $this; + } + + // We have active session + if(null !== $this->_session_data) + { + list($uid, $upw) = explode('.', $this->_session_data); + // Bad cookie - destroy session + if(empty($uid) || !is_numeric($uid) || empty($upw)) + { + $this->_destroyBadSession(); + $this->_initConstants(); + return $this; + } + + $udata = $this->_load($uid, $force); + // Bad cookie - destroy session + if(empty($udata)) + { + $this->_destroyBadSession(); + $this->_initConstants(); + return $this; + } + + // we have a match + if(md5($udata['user_password']) == $upw) + { + // set current user data + $this->setData($udata); + + // NEW - try 'logged in as' feature + if(!$denyAs) $this->loadAs(); + + $this->_initConstants(); + return $this; + } + + $this->_destroyBadSession(); + $this->_initConstants(); + return $this; + } - if (USER) - $this->setData(get_user_data(USERID)); return $this; } + final public function loadAs() + { + // FIXME - option to avoid it when browsing Admin area + $loginAs = $this->_getSessionDataAs(); + if(!$this->getParentId() && false !== $loginAs && $loginAs !== $this->getId() && $loginAs !== 1 && $this->isMainAdmin()) + { + $uasdata = $this->_load($loginAs); + if(!empty($uasdata)) + { + // backup parent user data to prevent further db queries + $this->_parent_id = $this->getId(); + $this->_parent_model = new e_system_user($this->getData()); + $this->setData($uasdata); + + // not allowed - revert back + if($this->isMainAdmin()) + { + $this->_parent_id = false; + $this->setData($this->_parent_model->getData()); + $this->_parent_model = null; + $this->_destroyAsSession(); + } + else + { + $this->_parent_extmodel = $this->_extended_model; + $this->_parent_extstruct = $this->_extended_structure; + $this->_user_config = $this->_parent_config; + $this->_extended_model = $this->_extended_structure = $this->_user_config = null; + } + } + } + else + { + $this->_parent_id = false; + $this->_parent_model = null; + $this->_parent_extstruct = $this->_parent_extmodel = null; + } + } + + final protected function _destroySession() + { + cookie($this->_session_key, '', (time() - 2592000)); + $_SESSION[$this->_session_key] = ''; + + return $this; + } + + final protected function _destroyAsSession() + { + $key = $this->_session_key.'_as'; + cookie($key, '', (time() - 2592000)); + $_SESSION[$key] = ''; + unset($_SESSION[$key]); + + return $this; + } + + final protected function _destroyBadSession() + { + $this->_session_error = true; + return $this->_destroySession(); + } + + final protected function _getSessionDataAs() + { + $id = false; + $key = $this->_session_key.'_as'; + + if('session' == $this->_session_type && isset($_SESSION[$key]) && !empty($_SESSION[$key])) + { + $id = $_SESSION[$key]; + } + elseif('cookie' == $this->_session_type && isset($_COOKIE[$key]) && !empty($_COOKIE[$key])) + { + $id = $_COOKIE[$key]; + } + + if(!empty($id) && is_numeric($id)) return intval($id); + + return false; + } + + final public function setSessionData($force = false) + { + if($force || null === $this->_session_data) + { + $this->_session_key = e107::getPref('cookie_name', 'e107cookie'); + $this->_session_type = e107::getPref('user_tracking', 'cookie'); + if('session' == $this->_session_type && isset($_SESSION[$this->_session_key]) && !empty($_SESSION[$this->_session_key])) + { + $this->_session_data = &$_SESSION[$this->_session_key]; + } + elseif('cookie' == $this->_session_type && isset($_COOKIE[$this->_session_key]) && !empty($_COOKIE[$this->_session_key])) + { + $this->_session_data = &$_COOKIE[$this->_session_key]; + } + } + + return $this; + } + + public function hasSessionError() + { + return $this->_session_error; + } + + + final protected function _load($user_id) + { + if(e107::getDb()->db_Select('user', '*', 'user_id='.intval($user_id))) + { + return e107::getDb()->db_Fetch(); + } + return array(); + } + /** * Not allowed * @@ -629,6 +975,14 @@ class e_user_extended_model extends e_front_model ->load(); } + /** + * Always return integer + */ + public function getId() + { + return (integer) parent::getId(); + } + /** * Get user model * @return e_user_model @@ -671,12 +1025,13 @@ class e_user_extended_model extends e_front_model /** * Get User extended field value * Returns NULL when field/default value not found or not enough permissions - * @param string$field + * @param string $field + * @param boolean $short if true, 'user_' prefix will be added to field name * @return mixed */ - public function getValue($field) + public function getValue($field, $short = true) { - $field = 'user_'.$field; + if($short) $field = 'user_'.$field; if (!$this->checkRead($field)) return null; return $this->get($field, $this->getDefault($field)); @@ -687,17 +1042,28 @@ class e_user_extended_model extends e_front_model * Note: Data is not sanitized! * @param string $field * @param mixed $value + * @param boolean $short if true, 'user_' prefix will be added to field name * @return e_user_extended_model */ - public function setValue($field, $value) + public function setValue($field, $value, $short = true) { - $field = 'user_'.$field; + if($short) $field = 'user_'.$field; if (!$this->checkWrite($field)) return $this; $this->set($field, $value, true); return $this; } + public function getReadData() + { + // TODO array allowed profile page data (read mode) + } + + public function getWriteData() + { + // TODO array allowed settings page data (edit mode) + } + /** * Get default field value, defined by extended field structure * Returns NULL if field/default value not found @@ -716,17 +1082,27 @@ class e_user_extended_model extends e_front_model */ public function checkRead($field) { - return $this->getEditor()->checkClass(varset($this->_struct_index[$field]['read'])); + $hidden = $this->get('user_hidden_fields'); + if($this->getId() !== $this->getEditor()->getId() && !empty($hidden) && strpos($hidden, $field) !== false) return false; + + return ($this->checkApplicable($field) && $this->getEditor()->checkClass(varset($this->_struct_index[$field]['read']))); } /** - * Check field write permissions + * Check field write permissions against current editor * @param string $field * @return boolean */ public function checkWrite($field) { - return $this->getEditor()->checkClass(varset($this->_struct_index[$field]['write'])); + if(!$this->checkApplicable($field)) return false; + + $editor = $this->getEditor(); + // Main admin checked later in checkClass() method + if($editor->checkAdminPerms('4') && varset($this->_struct_index[$field]['write']) != e_UC_NOBODY) + return true; + + return $editor->checkClass(varset($this->_struct_index[$field]['write'])); } /** @@ -736,17 +1112,17 @@ class e_user_extended_model extends e_front_model */ public function checkSignup($field) { - return $this->getEditor()->checkClass(varset($this->_struct_index[$field]['signup'])); + return $this->getUser()->checkClass(varset($this->_struct_index[$field]['signup'])); } /** - * Check field applicable permissions + * Check field applicable permissions against current user * @param string $field * @return boolean */ public function checkApplicable($field) { - return $this->getEditor()->checkClass(varset($this->_struct_index[$field]['applicable'])); + return $this->getUser()->checkClass(varset($this->_struct_index[$field]['applicable'])); } /** @@ -774,6 +1150,7 @@ class e_user_extended_model extends e_front_model { // load structure dependencies $ignore = array($this->getFieldIdName(), 'user_hidden_fields'); // TODO - user_hidden_fields? Old? + $fields = $struct_tree->getTree(); foreach ($fields as $id => $field) { @@ -849,9 +1226,28 @@ class e_user_extended_model extends e_front_model return $this->_structure; } + /** + * Additional security while applying posted + * data to user extended model + * @return e_user_extended_model + */ + public function mergePostedData() + { + $posted = $this->getPostedData(); + foreach ($posted as $key => $value) + { + if(!$this->checkWrite($key)) + { + $this->removePosted($key); + } + } + parent::mergePostedData(true, true, true); + return $this; + } + /** * Build data types and rules on the fly and save - * @see e107_handlers/e_front_model#save($from_post, $force, $session_messages) + * @see e_front_model::save() */ public function save($force = false, $session = false) { @@ -859,10 +1255,13 @@ class e_user_extended_model extends e_front_model return parent::save(true, $force, $session); } + /** + * Doesn't save anything actually... + */ public function saveDebug($retrun = false, $undo = true) { $this->_buildManageRules(); - parent::saveDebug($return, $undo); + return parent::saveDebug($return, $undo); } } @@ -1011,3 +1410,80 @@ class e_user_extended_structure_tree extends e_tree_model return $this; } } + +class e_user_pref extends e_model +{ + /** + * @var e_user_model + */ + protected $_user; + + /** + * Constructor + * @param e_user_model $user_model + * @return void + */ + public function __construct(e_user_model $user_model) + { + $this->_user = $user_model; + $this->load(); + } + + /** + * Load data from user preferences string + * @param boolean $force + * @return e_user_pref + */ + public function load($force = false) + { + if($force || !$this->hasData()) + { + $data = $this->_user->get('user_prefs', ''); + if(!empty($data)) + { + $data = e107::getArrayStorage()->ReadArray($data); + if(!$data) $data = array(); + } + else $data = array(); + + $this->setData($data); + } + return $this; + } + + /** + * Apply current data to user data + * @return e_user_pref + */ + public function apply() + { + $this->_user->set('user_prefs', $this->toString(true)); + return $this; + } + + /** + * Save and apply user preferences + * @return boolean success + */ + public function save() + { + if($this->_user->getId()) + { + $data = $this->toString(true); + $this->apply(); + return (e107::getDb('user_prefs')->db_Update('user', "user_prefs='{$data}' WHERE user_id=".$this->_user->getId()) ? true : false); + } + return false; + } + + /** + * Remove & apply user prefeferences, optionally - save to DB + * @return boolean success + */ + public function delete($save = false) + { + $this->removeData()->apply(); + if($save) return $this->save(); + return true; + } +}