Merge pull request #567 from kijin/pr/session-class

세션 처리 관련 기능 정리 및 개선
This commit is contained in:
Kijin Sung 2017-02-10 21:30:06 +09:00 committed by GitHub
commit 99cb67b5db
33 changed files with 1934 additions and 353 deletions

View file

@ -67,10 +67,12 @@ $lang->msg_blacklisted_addon = 'This addon has been disabled because it conflict
$lang->msg_blacklisted_widget = 'This widget has been disabled because it conflicts with a feature that Rhymix supports by default, or is known to have other compatibility problems.';
$lang->msg_blacklisted_layout = 'This layout has been disabled because it conflicts with a feature that Rhymix supports by default, or is known to have other compatibility problems.';
$lang->msg_blacklisted_reason['autolang'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigGeneral">System Settings</a> page.';
$lang->msg_blacklisted_reason['auto_login'] = 'The functionality that this module used to provide is included by default in Rhymix.';
$lang->msg_blacklisted_reason['errorlogger'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigDebug">Debug Settings</a> page.';
$lang->msg_blacklisted_reason['fix_mysql_utf8'] = 'The functionality that this addon used to provide is included by default in Rhymix.';
$lang->msg_blacklisted_reason['member_communication'] = 'The functionality that this addon used to provide has been moved to the member and ncenterlite modules.';
$lang->msg_blacklisted_reason['seo'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigSEO">SEO Settings</a> page.';
$lang->msg_blacklisted_reason['session_shield'] = 'The functionality that this addon used to provide is included by default in Rhymix.';
$lang->msg_blacklisted_reason['smartphone'] = 'This module was disabled in XE long before Rhymix even existed.';
$lang->msg_blacklisted_reason['zipperupper'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">Advanced Settings</a> page.';
$lang->msg_warning = 'Warning';

View file

@ -67,10 +67,12 @@ $lang->msg_blacklisted_addon = '이 애드온은 Rhymix에서 기본 제공하
$lang->msg_blacklisted_widget = '이 위젯은 Rhymix에서 기본 제공하는 기능과 충돌하거나 그 밖의 호환성 문제가 있으므로 사용이 중단되었습니다.';
$lang->msg_blacklisted_layout = '이 레이아웃은 Rhymix에서 기본 제공하는 기능과 충돌하거나 그 밖의 호환성 문제가 있으므로 사용이 중단되었습니다.';
$lang->msg_blacklisted_reason['autolang'] = '이 애드온에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigGeneral">시스템 설정</a> 페이지에서 관리할 수 있습니다.';
$lang->msg_blacklisted_reason['auto_login'] = '이 모듈에서 제공하던 기능은 Rhymix에 포함되어 있습니다.';
$lang->msg_blacklisted_reason['errorlogger'] = '이 모듈에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigDebug">디버그 설정</a> 페이지에서 관리할 수 있습니다.';
$lang->msg_blacklisted_reason['fix_mysql_utf8'] = '이 애드온에서 제공하던 기능은 Rhymix에 포함되어 있습니다.';
$lang->msg_blacklisted_reason['member_communication'] = '이 애드온에서 제공하던 기능은 알림센터 모듈에서 관리할 수 있습니다.';
$lang->msg_blacklisted_reason['seo'] = '이 모듈에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigSEO">SEO 설정</a> 페이지에서 관리할 수 있습니다.';
$lang->msg_blacklisted_reason['session_shield'] = '이 애드온에서 제공하던 기능은 Rhymix에 포함되어 있습니다.';
$lang->msg_blacklisted_reason['smartphone'] = '이 모듈은 XE에서도 사용되지 않고 있었습니다.';
$lang->msg_blacklisted_reason['zipperupper'] = '이 애드온에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">고급 설정</a> 페이지에서 관리할 수 있습니다.';
$lang->msg_warning = '경고';

View file

@ -16,6 +16,7 @@
<action name="dispMemberOwnDocument" type="view" standalone="true" />
<action name="dispMemberScrappedDocument" type="view" standalone="true" />
<action name="dispMemberSavedDocument" type="view" standalone="true" />
<action name="dispMemberActiveLogins" type="view" standalone="true" />
<action name="dispMemberFindAccount" type="view" standalone="true" />
<action name="dispMemberGetTempPassword" type="view" standalone="true" />
<action name="dispMemberResendAuthMail" type="view" standalone="true" />
@ -62,6 +63,7 @@
<action name="procMemberDeleteScrap" type="controller" standalone="true" />
<action name="procMemberSaveDocument" type="controller" standalone="true" />
<action name="procMemberDeleteSavedDocument" type="controller" standalone="true" />
<action name="procMemberDeleteAutologin" type="controller" standalone="true" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" standalone="true" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" standalone="true" />

View file

@ -107,6 +107,7 @@ $lang->cmd_manage_email_host = 'E-mail provider check';
$lang->cmd_manage_nick_name = 'Prohibited NickNames';
$lang->cmd_manage_form = 'Signup Form';
$lang->cmd_view_own_document = 'Written Articles';
$lang->cmd_view_active_logins = 'Active Logins';
$lang->cmd_manage_member_info = 'Manage Member Info';
$lang->cmd_trace_document = 'Trace Written Articles';
$lang->cmd_trace_comment = 'Trace Written Comments';
@ -160,6 +161,7 @@ $lang->cmd_config_password_strength = 'password strength';
$lang->cmd_password_hashing_algorithm = 'Password Hashing Algorithm';
$lang->cmd_password_hashing_work_factor = 'Password Hashing Work Factor';
$lang->cmd_password_hashing_auto_upgrade = 'Auto-upgrade Hashing Algorithm';
$lang->cmd_password_change_invalidate_other_sessions = 'Log out other devices on password change';
$lang->password_strength_low = 'low';
$lang->password_strength_normal = 'normal';
$lang->password_strength_high = 'high';
@ -167,6 +169,7 @@ $lang->about_password_strength_config = 'When members register or change the pas
$lang->about_password_hashing_algorithm = 'You can choose how to encrypt (hash) members\' passwords stored in the database.';
$lang->about_password_hashing_work_factor = 'Higher work factors are more secure, but logins may take a long time. This only applies to bcrypt and pbkdf2.';
$lang->about_password_hashing_auto_upgrade = 'Passwords encrypted using different algorithms will be automatically converted to the configured algorithm at next login.';
$lang->about_password_change_invalidate_other_sessions = 'Log out all other devices (browsers) when a member changes the password.';
$lang->about_password_strength['low'] = 'the password must be at least 4';
$lang->about_password_strength['normal'] = 'the password must be at least 6, and must have at least one alpha character and numeric characters';
$lang->about_password_strength['high'] = 'the password must be at least 8, and must have at least one alpha character, numeric character and special character ';
@ -297,3 +300,6 @@ $lang->spammer_description = '<p>Spam user management. This function denied user
$lang->btn_spammer_delete_all = 'Delete all';
$lang->spammer_move_to_trash = 'Move to trash';
$lang->msg_spammer_complete = 'Completed.';
$lang->cmd_login_browser_info = 'Browser Information';
$lang->cmd_initial_login = 'First Login';
$lang->cmd_recent_visit = 'Recent Visit';

View file

@ -109,6 +109,7 @@ $lang->cmd_manage_email_host = '이메일 제공자 관리';
$lang->cmd_manage_nick_name = '금지 닉네임 관리';
$lang->cmd_manage_form = '가입 폼 관리';
$lang->cmd_view_own_document = '작성 글 보기';
$lang->cmd_view_active_logins = '로그인 관리';
$lang->cmd_manage_member_info = '회원 정보 관리';
$lang->cmd_trace_document = '작성글 추적';
$lang->cmd_trace_comment = '작성 댓글 추적';
@ -169,6 +170,7 @@ $lang->cmd_config_password_strength = '비밀번호 보안수준';
$lang->cmd_password_hashing_algorithm = '비밀번호 암호화 알고리듬';
$lang->cmd_password_hashing_work_factor = '비밀번호 암호화 소요시간';
$lang->cmd_password_hashing_auto_upgrade = '알고리듬 자동 업그레이드';
$lang->cmd_password_change_invalidate_other_sessions = '비번 변경시 다른 기기 로그아웃';
$lang->password_strength_low = '낮음';
$lang->password_strength_normal = '보통';
$lang->password_strength_high = '높음';
@ -176,6 +178,7 @@ $lang->about_password_strength_config = '회원들이 비밀번호를 등록/변
$lang->about_password_hashing_algorithm = '회원들의 비밀번호를 DB에 저장할 때 암호화(해싱)하는 방식을 지정할 수 있습니다.';
$lang->about_password_hashing_work_factor = '시간이 오래 걸리는 알고리듬일수록 보안이 강하지만, 로그인이 오래 걸릴 수 있습니다. bcrypt 및 pbkdf2 알고리듬에만 적용됩니다.';
$lang->about_password_hashing_auto_upgrade = '설정된 알고리듬과 다른 방법으로 암호화된 비밀번호가 있으면 다음 로그인시 설정된 알고리듬으로 자동 변환합니다.';
$lang->about_password_change_invalidate_other_sessions = '비밀번호를 변경하면 현재 기기(브라우저)를 제외한 모든 로그인이 풀리도록 합니다.';
$lang->about_password_strength['low'] = '비밀번호는 4자 이상이어야 합니다.';
$lang->about_password_strength['normal'] = '비밀번호는 6자리 이상이어야 하며 영문과 숫자를 반드시 포함해야 합니다.';
$lang->about_password_strength['high'] = '비밀번호는 8자리 이상이어야 하며 영문과 숫자, 특수문자를 반드시 포함해야 합니다.';
@ -317,3 +320,6 @@ $lang->spammer_move_to_trash = '휴지통으로 이동';
$lang->msg_spammer_complete = '완료되었습니다.';
$lang->nick_name_before_changing = '닉네임 변경 전';
$lang->nick_name_after_changing = '닉네임 변경 후';
$lang->cmd_login_browser_info = '브라우저 정보';
$lang->cmd_initial_login = '최초 로그인';
$lang->cmd_recent_visit = '최근 방문';

View file

@ -171,6 +171,7 @@ class memberAdminController extends member
'password_hashing_algorithm',
'password_hashing_work_factor',
'password_hashing_auto_upgrade',
'password_change_invalidate_other_sessions',
'update_nickname_log',
'member_allow_fileupload'
);

View file

@ -204,6 +204,9 @@ class member extends ModuleObject {
if(!$oDB->isColumnExists("member", "list_order")) return true;
if(!$oDB->isIndexExists("member","idx_list_order")) return true;
// Check autologin table
if(!$oDB->isColumnExists("member_autologin", "security_key")) return true;
$oModuleModel = getModel('module');
$config = $oModuleModel->getModuleConfig('member');
@ -314,6 +317,13 @@ class member extends ModuleObject {
{
$oDB->addIndex("member","idx_list_order", array("list_order"));
}
// Check autologin table
if(!$oDB->isColumnExists("member_autologin", "security_key"))
{
$oDB->dropTable('member_autologin');
$oDB->createTableByXmlFile($this->module_path . '/schemas/member_autologin.xml');
}
$oModuleModel = getModel('module');
$config = $oModuleModel->getModuleConfig('member');

View file

@ -101,7 +101,9 @@ class memberController extends member
if(!$trigger_output->toBool()) return $trigger_output;
// Destroy session information
Rhymix\Framework\Session::logout();
$this->destroySessionInfo();
$this->_clearMemberCache($logged_info->member_srl);
// Call a trigger after log-out (after)
ModuleHandler::triggerCall('member.doLogout', 'after', $logged_info);
@ -111,9 +113,9 @@ class memberController extends member
$oModuleModel = getModel('module');
$config = $oModuleModel->getModuleConfig('member');
if($config->after_logout_url)
{
$output->redirect_url = $config->after_logout_url;
$this->_clearMemberCache($logged_info->member_srl);
}
return $output;
}
@ -202,6 +204,52 @@ class memberController extends member
$oDocumentController = getController('document');
$oDocumentController->deleteDocument($document_srl, true);
}
/**
* Delete an autologin
*/
function procMemberDeleteAutologin()
{
// Check login information
if(!Context::get('is_logged')) return new Object(-1, 'msg_not_logged');
$logged_info = Context::get('logged_info');
$autologin_id = intval(Context::get('autologin_id'));
$autologin_key = Context::get('autologin_key');
if (!$autologin_id || !$autologin_key)
{
return new Object(-1, 'msg_invalid_request');
}
$args = new stdClass;
$args->autologin_id = $autologin_id;
$args->autologin_key = $autologin_key;
$output = executeQueryArray('member.getAutologin', $args);
if ($output->toBool() && $output->data)
{
$autologin_info = array_first($output->data);
if ($autologin_info->member_srl == $logged_info->member_srl)
{
$output = executeQuery('member.deleteAutologin', $args);
if ($output->toBool())
{
$this->add('deleted', 'Y');
}
else
{
$this->add('deleted', 'N');
}
}
else
{
$this->add('deleted', 'N');
}
}
else
{
$this->add('deleted', 'N');
}
}
/**
* Check values when member joining
@ -687,6 +735,14 @@ class memberController extends member
$args->password = $password;
$output = $this->updateMemberPassword($args);
if(!$output->toBool()) return $output;
// Log out all other sessions.
$oModuleModel = getModel('module');
$member_config = $oModuleModel->getModuleConfig('member');
if ($member_config->password_change_invalidate_other_sessions === 'Y')
{
Rhymix\Framework\Session::destroyOtherSessions($member_srl);
}
$this->add('member_srl', $args->member_srl);
$this->setMessage('success_updated');
@ -723,7 +779,8 @@ class memberController extends member
$output = $this->deleteMember($member_srl);
if(!$output->toBool()) return $output;
// Destroy all session information
$this->destroySessionInfo();
executeQuery('member.deleteAutologin', (object)array('member_srl' => $member_srl));
Rhymix\Framework\Session::logout();
// Return success message
$this->setMessage('success_leaved');
@ -1621,82 +1678,56 @@ class memberController extends member
/**
* Auto-login
*
* @return void
* @param string $autologin_key
* @return int|false
*/
function doAutologin()
function doAutologin($autologin_key = null)
{
// Get a key value of auto log-in
$args = new stdClass;
$args->autologin_key = $_COOKIE['xeak'];
// Get information of the key
$output = executeQuery('member.getAutologin', $args);
// If no information exists, delete a cookie
if(!$output->toBool() || !$output->data)
// Validate the key.
if (strlen($autologin_key) == 48)
{
setCookie('xeak',null,$_SERVER['REQUEST_TIME']+60*60*24*365, '/');
return;
}
$oMemberModel = getModel('member');
$config = $oMemberModel->getMemberConfig();
$user_id = ($config->identifier == 'user_id') ? $output->data->user_id : $output->data->email_address;
$password = $output->data->password;
if(!$user_id || !$password)
{
setCookie('xeak',null,$_SERVER['REQUEST_TIME']+60*60*24*365, '/');
return;
}
$do_auto_login = false;
// Compare key values based on the information
$check_key = strtolower($user_id).$password.$_SERVER['HTTP_USER_AGENT'];
$check_key = substr(hash_hmac('sha256', $check_key, substr($args->autologin_key, 0, 32)), 0, 32);
if($check_key === substr($args->autologin_key, 32))
{
// Check change_password_date
$oModuleModel = getModel('module');
$member_config = $oModuleModel->getModuleConfig('member');
$limit_date = $member_config->change_password_date;
// Check if change_password_date is set
if($limit_date > 0)
{
$oMemberModel = getModel('member');
$columnList = array('member_srl', 'change_password_date');
if($config->identifier == 'user_id')
{
$member_info = $oMemberModel->getMemberInfoByUserID($user_id, $columnList);
}
else
{
$member_info = $oMemberModel->getMemberInfoByEmailAddress($user_id, $columnList);
}
if($member_info->change_password_date >= date('YmdHis', strtotime('-'.$limit_date.' day')) ){
$do_auto_login = true;
}
}
else
{
$do_auto_login = true;
}
}
if($do_auto_login)
{
$output = $this->doLogin($user_id);
$security_key = substr($autologin_key, 24, 24);
$autologin_key = substr($autologin_key, 0, 24);
}
else
{
executeQuery('member.deleteAutologin', $args);
setCookie('xeak',null,$_SERVER['REQUEST_TIME']+60*60*24*365, '/');
return false;
}
// Fetch autologin information from DB.
$args = new stdClass;
$args->autologin_key = $autologin_key;
$output = executeQuery('member.getAutologin', $args);
if (!$output->toBool() || !$output->data)
{
return false;
}
if (is_array($output->data))
{
$output->data = array_first($output->data);
}
// Check the security key.
if ($output->data->security_key !== $security_key || !$output->data->member_srl)
{
$args = new stdClass;
$args->autologin_key = $autologin_key;
executeQuery('member.deleteAutologin', $args);
return false;
}
// Update the security key.
$new_security_key = Rhymix\Framework\Security::getRandom(24, 'alnum');
$args = new stdClass;
$args->security_key = $new_security_key;
$update_output = executeQuery('member.updateAutologin', $args);
if ($update_output->toBool())
{
Rhymix\Framework\Session::setAutologinKeys($autologin_key, $new_security_key);
}
// Return the member_srl.
return intval($output->data->member_srl);
}
/**
@ -1858,19 +1889,21 @@ class memberController extends member
// When user checked to use auto-login
if($keep_signed)
{
// Key generate for auto login
$random_key = Rhymix\Framework\Security::getRandom(32, 'hex');
$extra_key = strtolower($user_id).$this->memberInfo->password.$_SERVER['HTTP_USER_AGENT'];
$extra_key = substr(hash_hmac('sha256', $extra_key, $random_key), 0, 32);
$random_key = Rhymix\Framework\Security::getRandom(48, 'alnum');
$autologin_args = new stdClass;
$autologin_args->autologin_key = $random_key.$extra_key;
$autologin_args->autologin_key = substr($random_key, 0, 24);
$autologin_args->security_key = substr($random_key, 24, 24);
$autologin_args->member_srl = $this->memberInfo->member_srl;
executeQuery('member.deleteAutologin', $autologin_args);
$autologin_args->user_agent = json_encode(Rhymix\Framework\UA::getBrowserInfo());
$autologin_output = executeQuery('member.insertAutologin', $autologin_args);
if($autologin_output->toBool()) setCookie('xeak',$autologin_args->autologin_key, $_SERVER['REQUEST_TIME']+31536000, '/');
if ($autologin_output->toBool())
{
Rhymix\Framework\Session::setAutologinKeys(substr($random_key, 0, 24), substr($random_key, 24, 24));
}
}
$this->setSessionInfo();
Rhymix\Framework\Session::login($this->memberInfo->member_srl);
return $output;
}
@ -1881,29 +1914,23 @@ class memberController extends member
{
$oMemberModel = getModel('member');
$config = $oMemberModel->getMemberConfig();
// If your information came through the current session information to extract information from the users
if(!$this->memberInfo && $_SESSION['member_srl'] && $oMemberModel->isLogged() )
if(!$this->memberInfo && Rhymix\Framework\Session::getMemberSrl())
{
$this->memberInfo = $oMemberModel->getMemberInfoByMemberSrl($_SESSION['member_srl']);
// If you do not destroy the session Profile
if($this->memberInfo->member_srl != $_SESSION['member_srl'])
{
$this->destroySessionInfo();
return;
}
$this->memberInfo = Rhymix\Framework\Session::getMemberInfo();
}
// Stop using the session id is destroyed
if($this->memberInfo->denied=='Y')
if(!$this->memberInfo->member_srl)
{
$this->destroySessionInfo();
return;
}
// Log in for treatment sessions set
/*
$_SESSION['is_logged'] = true;
$_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['member_srl'] = $this->memberInfo->member_srl;
$_SESSION['member_srl'] = $_SESSION['RHYMIX']['login'] = $this->memberInfo->member_srl;
$_SESSION['is_admin'] = '';
setcookie('xe_logged', 'true', 0, '/');
*/
// Do not save your password in the session jiwojum;;
//unset($this->memberInfo->password);
// User Group Settings
@ -1927,6 +1954,7 @@ class memberController extends member
$this->addMemberMenu( 'dispMemberScrappedDocument', 'cmd_view_scrapped_document');
$this->addMemberMenu( 'dispMemberSavedDocument', 'cmd_view_saved_document');
$this->addMemberMenu( 'dispMemberOwnDocument', 'cmd_view_own_document');
$this->addMemberMenu( 'dispMemberActiveLogins', 'cmd_view_active_logins');
if($config->update_nickname_log == 'Y')
{
$this->addMemberMenu( 'dispMemberModifyNicknameLog', 'cmd_modify_nickname_log');
@ -2618,19 +2646,11 @@ class memberController extends member
$_SESSION[$key] = '';
}
session_destroy();
Rhymix\Framework\Session::destroy();
setcookie(session_name(), '', $_SERVER['REQUEST_TIME']-42000, '/');
setcookie('sso','',$_SERVER['REQUEST_TIME']-42000, '/');
setcookie('xeak','',$_SERVER['REQUEST_TIME']-42000, '/');
setcookie('xe_logged', 'false', $_SERVER['REQUEST_TIME'] - 42000, '/');
if($memberSrl || $_COOKIE['xeak'])
{
$args = new stdClass();
$args->member_srl = $memberSrl;
$args->autologin_key = $_COOKIE['xeak'];
$output = executeQuery('member.deleteAutologin', $args);
}
}
function _updatePointByGroup($memberSrl, $groupSrlList)

View file

@ -231,36 +231,9 @@ class memberModel extends member
/**
* @brief Check if logged-in
*/
function isLogged() {
if($_SESSION['is_logged'])
{
if(Mobile::isFromMobilePhone())
{
return true;
}
elseif(filter_var($_SESSION['ipaddress'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
// IPv6: require same /48
if(strncmp(inet_pton($_SESSION['ipaddress']), inet_pton($_SERVER['REMOTE_ADDR']), 6) == 0)
{
return true;
}
}
else
{
// IPv4: require same /24
if(ip2long($_SESSION['ipaddress']) >> 8 == ip2long($_SERVER['REMOTE_ADDR']) >> 8)
{
return true;
}
}
}
if(Context::getSessionStatus())
{
$_SESSION['is_logged'] = false;
}
return false;
function isLogged()
{
return Rhymix\Framework\Session::getMemberSrl() ? true : false;
}
/**
@ -268,39 +241,7 @@ class memberModel extends member
*/
function getLoggedInfo()
{
// Return session info if session info is requested and the user is logged-in
if($this->isLogged())
{
$logged_info = Context::get('logged_info');
// Admin/Group list defined depending on site_module_info
$site_module_info = Context::get('site_module_info');
if($site_module_info->site_srl)
{
$logged_info->group_list = $this->getMemberGroups($logged_info->member_srl, $site_module_info->site_srl);
// Add is_site_admin bool variable into logged_info if site_administrator is
$oModuleModel = getModel('module');
if($oModuleModel->isSiteAdmin($logged_info)) $logged_info->is_site_admin = true;
else $logged_info->is_site_admin = false;
}
else
{
// Register a default group if the site doesn't have a member group
if(count($logged_info->group_list) === 0)
{
$default_group = $this->getDefaultGroup(0);
$oMemberController = getController('member');
$oMemberController->addMemberToGroup($logged_info->member_srl, $default_group->group_srl, 0);
$groups[$default_group->group_srl] = $default_group->title;
$logged_info->group_list = $groups;
}
$logged_info->is_site_admin = false;
}
Context::set('logged_info', $logged_info);
return $logged_info;
}
return new stdClass;
return Context::get('logged_info');
}
/**
@ -398,6 +339,7 @@ class memberModel extends member
}
$info->signature = $this->getSignature($info->member_srl);
$info->group_list = $this->getMemberGroups($info->member_srl, $site_srl);
$info->is_site_admin = $oModuleModel->isSiteAdmin($info) ? true : false;
$extra_vars = unserialize($info->extra_vars);
unset($info->extra_vars);
@ -493,8 +435,7 @@ class memberModel extends member
*/
function getLoggedMemberSrl()
{
if(!$this->isLogged()) return;
return $_SESSION['member_srl'];
return Rhymix\Framework\Session::getMemberSrl();
}
/**
@ -527,6 +468,12 @@ class memberModel extends member
$args->site_srl = $site_srl;
$output = executeQueryArray('member.getMemberGroups', $args);
$group_list = $output->data;
if (!count($group_list))
{
$default_group = $this->getDefaultGroup($site_srl);
getController('member')->addMemberToGroup($member_srl, $default_group->group_srl, $site_srl);
$group_list[$default_group->group_srl] = $default_group->title;
}
//insert in cache
Rhymix\Framework\Cache::set($cache_key, $group_list, 0, true);
}

View file

@ -430,6 +430,30 @@ class memberView extends member
$this->setTemplateFile('saved_list');
}
/**
* @brief Display the login management page
*/
function dispMemberActiveLogins()
{
$logged_info = Context::get('logged_info');
if (!$logged_info->member_srl)
{
return $this->stop('msg_not_logged');
}
$args = new stdClass();
$args->member_srl = $logged_info->member_srl;
$args->page = (int)Context::get('page');
$output = executeQueryArray('member.getAutologin', $args);
Context::set('total_count', $output->total_count);
Context::set('total_page', $output->total_page);
Context::set('page', $output->page);
Context::set('active_logins', $output->data);
Context::set('page_navigation', $output->page_navigation);
$this->setTemplateFile('active_logins');
}
/**
* @brief Display the login form
*/

View file

@ -3,7 +3,8 @@
<table name="member_autologin" />
</tables>
<conditions>
<condition operation="equal" column="autologin_key" var="autologin_key" pipe="or" default='' />
<condition operation="equal" column="member_srl" var="member_srl" pipe="or" />
<condition operation="equal" column="autologin_key" var="autologin_key" />
<condition operation="notequal" column="autologin_key" var="not_autologin_key" pipe="and" />
<condition operation="equal" column="member_srl" var="member_srl" pipe="and" />
</conditions>
</query>

View file

@ -4,13 +4,21 @@
<table name="member_autologin" alias="member_autologin" />
</tables>
<columns>
<column name="member_autologin.*" />
<column name="member.email_address" alias="email_address" />
<column name="member.user_id" alias="user_id" />
<column name="member.password" alias="password" />
<column name="member_autologin.autologin_key" alias="autologin_key" />
</columns>
<conditions>
<condition operation="equal" column="member_autologin.autologin_key" var="autologin_key" notnull="notnull" />
<condition operation="equal" column="member.member_srl" var="member_autologin.member_srl" notnull="notnull" pipe="and" />
<condition operation="equal" column="member.member_srl" var="member_autologin.member_srl" />
<condition operation="equal" column="member_autologin.id" var="autologin_id" pipe="and" />
<condition operation="equal" column="member_autologin.autologin_key" var="autologin_key" pipe="and" />
<condition operation="equal" column="member_autologin.member_srl" var="member_srl" pipe="and" />
</conditions>
<navigation>
<index var="sort_index" default="member_autologin.id" order="asc" />
<list_count var="list_count" default="20" />
<page_count var="page_count" default="10" />
<page var="page" default="1" />
</navigation>
</query>

View file

@ -1,9 +1,15 @@
<query id="insertAutologin" action="insert">
<tables>
<table name="member_autologin" />
</tables>
<columns>
<column name="autologin_key" var="autologin_key" notnull="notnull" minlength="1" maxlength="60" />
<column name="member_srl" var="member_srl" filter="number" notnull="notnull" />
</columns>
<tables>
<table name="member_autologin" />
</tables>
<columns>
<column name="autologin_key" var="autologin_key" notnull="notnull" minlength="1" maxlength="80" />
<column name="security_key" var="security_key" notnull="notnull" minlength="1" maxlength="80" />
<column name="member_srl" var="member_srl" notnull="notnull" filter="number" />
<column name="regdate" var="regdate" default="curdate()" />
<column name="ipaddress" var="ipaddress" default="ipaddress()" />
<column name="last_visit" var="last_visit" default="curdate()" />
<column name="last_ipaddress" var="last_ipaddress" default="ipaddress()" />
<column name="user_agent" var="user_agent" />
</columns>
</query>

View file

@ -0,0 +1,12 @@
<query id="updateAutologin" action="update">
<tables>
<table name="member_autologin" />
</tables>
<columns>
<column name="security_key" var="security_key" />
<column name="member_srl" var="member_srl" filter="number" />
<column name="last_visit" var="last_visit" default="curdate()" />
<column name="last_ipaddress" var="last_ipaddress" default="ipaddress()" />
<column name="user_agent" var="user_agent" />
</columns>
</query>

View file

@ -1,4 +1,11 @@
<table name="member_autologin">
<column name="autologin_key" type="varchar" size="80" notnull="notnull" unique="unique_key" />
<column name="member_srl" type="number" size="11" notnull="notnull" unique="unique_key" />
<column name="id" type="number" primary_key="primary_key" auto_increment="auto_increment" />
<column name="autologin_key" type="varchar" size="80" notnull="notnull" unique="unique_autologin_key" />
<column name="security_key" type="varchar" size="80" notnull="notnull" />
<column name="member_srl" type="number" notnull="notnull" index="idx_member_srl" />
<column name="regdate" type="date" index="idx_regdate" />
<column name="ipaddress" type="varchar" size="80" />
<column name="last_visit" type="date" index="idx_last_visited" />
<column name="last_ipaddress" type="varchar" size="80" />
<column name="user_agent" type="varchar" size="500" />
</table>

View file

@ -0,0 +1,46 @@
<include target="./common_header.html" />
<h1>{$member_title = $lang->cmd_view_active_logins }</h1>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{$lang->no}</th>
<th class="title">{$lang->cmd_login_browser_info}</th>
<th>{$lang->cmd_initial_login}</th>
<th>{$lang->cmd_recent_visit}</th>
<th>{$lang->cmd_delete}</th>
</tr>
</thead>
<tbody>
<tr loop="$active_logins => $no, $autologin_info">
{@ $autologin_info->user_agent = @json_decode($autologin_info->user_agent) ?: new stdClass()}
<td>{$no}</td>
<td class="title">
{escape($autologin_info->user_agent->browser)} {escape($autologin_info->user_agent->version)}<br />
{escape($autologin_info->user_agent->os)} {$autologin_info->user_agent->is_tablet ? 'Tablet' : ($autologin_info->user_agent->is_mobile ? 'Mobile' : 'PC')}
</td>
<td>{zdate($autologin_info->regdate, 'Y-m-d H:i')}<br />{$autologin_info->ipaddress}</td>
<td>{zdate($autologin_info->last_visit, 'Y-m-d H:i')}<br />{$autologin_info->last_ipaddress}</td>
<td><button class="delele_autologin" data-autologin-id="{$autologin_info->id}" data-autologin-key="{$autologin_info->autologin_key}">{$lang->cmd_delete}</button></td>
</tr>
</tbody>
</table>
<div class="pagination pagination-centered">
<ul>
<li><a href="{getUrl('page','','module_srl','')}" class="direction">&laquo; {$lang->first_page}</a></li>
<!--@while($page_no = $page_navigation->getNextPage())-->
<li class="active"|cond="$page == $page_no"><a href="{getUrl('page',$page_no,'module_srl','')}">{$page_no}</a></li>
<!--@end-->
<li><a href="{getUrl('page',$page_navigation->last_page,'module_srl','')}" class="direction">{$lang->last_page} &raquo;</a></li>
</ul>
</div>
<script>
$(function() {
$("button.delele_autologin").on("click", function(event) {
event.preventDefault();
exec_json('member.procMemberDeleteAutologin', { autologin_id: $(this).data("autologin-id"), autologin_key: $(this).data("autologin-key") }, function(data) {
window.location.reload();
});
});
});
</script>
<include target="./common_footer.html" />

View file

@ -0,0 +1,47 @@
<include target="./common_header.html" />
<section class="sw-body">
<h1>{$member_title = lang('member.cmd_view_active_logins')}</h1>
<div class="rx_member-notice">
{lang('common.total')}: {number_format($total_count)}
</div>
<div>
<ul class="rx_sw_list">
<li loop="$active_logins => $no, $autologin_info">
{@ $autologin_info->user_agent = @json_decode($autologin_info->user_agent) ?: new stdClass()}
<span class="content_basic">
<span class="content_title">
{escape($autologin_info->user_agent->browser)} {escape($autologin_info->user_agent->version)} ({escape($autologin_info->user_agent->os)})
</span>
<span class="content_subinfo">
{zdate($autologin_info->regdate, 'Y-m-d H:i')} ({$autologin_info->ipaddress})
</span>
<a class="delele_autologin" data-autologin-id="{$autologin_info->id}" data-autologin-key="{$autologin_info->autologin_key}" href="#">{$lang->cmd_delete}</a>
</span>
</li>
</ul>
</div>
</section>
<div class="pagination pagination-centered">
<ul>
<li><a href="{getUrl('page','','module_srl','')}" class="direction">&lsaquo; {lang('common.first_page')}</a></li>
<!--@while($page_no = $page_navigation->getNextPage())-->
<li class="active"|cond="$page == $page_no"><a href="{getUrl('page',$page_no,'module_srl','')}">{$page_no}</a></li>
<!--@end-->
<li><a href="{getUrl('page',$page_navigation->last_page,'module_srl','')}" class="direction">{lang('common.last_page')} &rsaquo;</a></li>
</ul>
</div>
<div class="sw-footer sw-anchor-buttons">
<a href="{getUrl('','module','module','act','dispModuleSelectList','id','target_module','type','single')}" onclick="popopen(this.href,'ModuleSelect');return false;">{lang('module.cmd_find_module')}</a>
<a href="{getUrl('selected_module_srl','')}" cond="$selected_module_srl">{lang('common.cmd_cancel')}</a>
</div>
<script>
$(function() {
$("a.delele_autologin").on("click", function(event) {
event.preventDefault();
exec_json('member.procMemberDeleteAutologin', { autologin_id: $(this).data("autologin-id"), autologin_key: $(this).data("autologin-key") }, function(data) {
window.location.reload();
});
});
});
</script>
<include target="./common_footer.html" />

View file

@ -71,6 +71,14 @@
<p class="x_help-block">{$lang->about_password_hashing_auto_upgrade}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_password_change_invalidate_other_sessions}</label>
<div class="x_controls">
<label for="password_change_invalidate_other_sessions_y" class="x_inline"><input type="radio" name="password_change_invalidate_other_sessions" id="password_change_invalidate_other_sessions_y" value="Y" checked="checked"|cond="$config->password_change_invalidate_other_sessions == 'Y'" /> {$lang->cmd_yes}</label>
<label for="password_change_invalidate_other_sessions_n" class="x_inline"><input type="radio" name="password_change_invalidate_other_sessions" id="password_change_invalidate_other_sessions_n" value="N" checked="checked"|cond="$config->password_change_invalidate_other_sessions != 'Y'" /> {$lang->cmd_no}</label>
<p class="x_help-block">{$lang->about_password_change_invalidate_other_sessions}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="webmaster_name">{$lang->webmaster_name}</label>
<div class="x_controls">