Merge pull request #232 from kijin/pr/cleanup-ip-allow-deny

관리자 허용 IP 및 사이트 잠금 예외 IP 처리 개선
This commit is contained in:
Kijin Sung 2016-02-09 11:41:06 +09:00
commit 987b78eb1a
33 changed files with 353 additions and 117 deletions

View file

@ -242,12 +242,6 @@ class Context
// Load system configuration.
$this->loadDBInfo();
// If the site is locked, display the locked page.
if(config('lock.locked'))
{
self::enforceSiteLock();
}
// If Rhymix is installed, get virtual site information.
if(self::isInstalled())
{
@ -370,7 +364,7 @@ class Context
}
}
}
// set locations for javascript use
$current_url = $request_uri = self::getRequestUri();
if ($_SERVER['REQUEST_METHOD'] == 'GET' && $this->get_vars)
@ -390,6 +384,12 @@ class Context
}
self::set('current_url', $current_url);
self::set('request_uri', $request_uri);
// If the site is locked, display the locked page.
if(config('lock.locked'))
{
self::enforceSiteLock();
}
}
/**
@ -1429,6 +1429,18 @@ class Context
*/
private static function enforceSiteLock()
{
// Allow if the current user is logged in as administrator, or trying to log in.
$logged_info = self::get('logged_info');
if ($logged_info && $logged_info->is_admin === 'Y')
{
return;
}
elseif (in_array(self::get('act'), array('procMemberLogin', 'dispMemberLogout')))
{
return;
}
// Allow if the current user is in the list of allowed IPs.
$allowed_list = config('lock.allow');
foreach ($allowed_list as $allowed_ip)
{
@ -1438,18 +1450,27 @@ class Context
}
}
// Set headers and constants for backward compatibility.
header('HTTP/1.1 503 Service Unavailable');
define('_XE_SITELOCK_', TRUE);
define('_XE_SITELOCK_TITLE_', config('lock.title'));
define('_XE_SITELOCK_MESSAGE_', config('lock.message'));
unset($_SESSION['XE_VALIDATOR_RETURN_URL']);
header("HTTP/1.1 403 Forbidden");
// Load the sitelock template.
if(FileHandler::exists(RX_BASEDIR . 'common/tpl/sitelock.user.html'))
{
include RX_BASEDIR . 'common/tpl/sitelock.user.html';
}
else
{
include RX_BASEDIR . 'common/tpl/sitelock.html';
$oMessageObject = getView('message');
$oMessageObject->setHttpStatusCode(503);
$oMessageObject->setError(-1);
$oMessageObject->setMessage(config('lock.title'));
$oMessageObject->dispMessage();
$oModuleHandler = new ModuleHandler;
$oModuleHandler->displayContent($oMessageObject);
}
exit;
}

View file

@ -436,18 +436,6 @@ class ModuleHandler extends Handler
$logged_info = Context::get('logged_info');
// Admin ip
if($kind == 'admin' && $_SESSION['denied_admin'] == 'Y')
{
self::_setInputErrorToContext();
$this->error = "msg_not_permitted_act";
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
}
// if(type == view, and case for using mobilephone)
if($type == "view" && Mobile::isFromMobilePhone() && Context::isInstalled())
{

View file

@ -174,7 +174,6 @@ class IpFilter
$range .= str_repeat('.*', 4 - $count);
}
$range = str_replace(array('.', '*'), array('\\.', '\\d+'), trim($range));
var_dump($ip, $range);
return preg_match("/^$range$/", $ip) ? true : false;
}

View file

@ -584,7 +584,23 @@ class adminAdminController extends admin
if (!IpFilter::validate($whitelist)) {
return new Object(-1, 'msg_invalid_ip');
}
$denied_ip = array_map('trim', preg_split('/[\r\n]/', $vars->admin_denied_ip));
$denied_ip = array_unique(array_filter($denied_ip, function($item) {
return $item !== '';
}));
if (!IpFilter::validate($whitelist)) {
return new Object(-1, 'msg_invalid_ip');
}
$oMemberAdminModel = getAdminModel('member');
if (!$oMemberAdminModel->getMemberAdminIPCheck($allowed_ip, $denied_ip))
{
return new Object(-1, 'msg_current_ip_will_be_denied');
}
Rhymix\Framework\Config::set('admin.allow', array_values($allowed_ip));
Rhymix\Framework\Config::set('admin.deny', array_values($denied_ip));
// Save
Rhymix\Framework\Config::save();
@ -646,9 +662,34 @@ class adminAdminController extends admin
$allowed_ip = array_unique(array_filter($allowed_ip, function($item) {
return $item !== '';
}));
if (!in_array(RX_CLIENT_IP, $allowed_ip)) array_unshift($allowed_ip, RX_CLIENT_IP);
if (!in_array('127.0.0.1', $allowed_ip)) array_unshift($allowed_ip, '127.0.0.1');
if (!IpFilter::validate($whitelist)) {
if ($vars->sitelock_locked === 'Y')
{
$allowed_localhost = false;
$allowed_current = false;
foreach ($allowed_ip as $range)
{
if (Rhymix\Framework\IpFilter::inRange('127.0.0.1', $range))
{
$allowed_localhost = true;
}
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range))
{
$allowed_current = true;
}
}
if (!$allowed_localhost)
{
array_unshift($allowed_ip, '127.0.0.1');
}
if (!$allowed_current)
{
array_unshift($allowed_ip, RX_CLIENT_IP);
}
}
if (!IpFilter::validate($whitelist))
{
return new Object(-1, 'msg_invalid_ip');
}

View file

@ -471,9 +471,28 @@ class adminAdminView extends admin
Context::set('sitelock_title', escape(Rhymix\Framework\Config::get('lock.title')));
Context::set('sitelock_message', escape(Rhymix\Framework\Config::get('lock.message')));
$allowed_ip = Rhymix\Framework\Config::get('lock.allow');
if (!in_array('127.0.0.1', $allowed_ip)) $allowed_ip[] = '127.0.0.1';
if (!in_array(RX_CLIENT_IP, $allowed_ip)) $allowed_ip[] = RX_CLIENT_IP;
$allowed_ip = Rhymix\Framework\Config::get('lock.allow') ?: array();
$allowed_localhost = false;
$allowed_current = false;
foreach ($allowed_ip as $range)
{
if (Rhymix\Framework\IpFilter::inRange('127.0.0.1', $range))
{
$allowed_localhost = true;
}
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range))
{
$allowed_current = true;
}
}
if (!$allowed_localhost)
{
array_unshift($allowed_ip, '127.0.0.1');
}
if (!$allowed_current)
{
array_unshift($allowed_ip, RX_CLIENT_IP);
}
Context::set('sitelock_allowed_ip', implode(PHP_EOL, $allowed_ip));
Context::set('remote_addr', RX_CLIENT_IP);

View file

@ -1,5 +1,6 @@
<?php
$lang->admin = 'Admin';
$lang->cmd_configure = 'Configure';
$lang->subtitle_primary = 'General Settings';
$lang->subtitle_security = 'Security Settings';
$lang->subtitle_advanced = 'Advanced Settings';
@ -115,9 +116,12 @@ $lang->input_footer_script = 'Footer script';
$lang->detail_input_footer_script = 'The script is inserted into the bottom of body. It does not work at admin page.';
$lang->corp = 'Crop(Cut)';
$lang->ratio = 'Ratio(Keep Aspect)';
$lang->admin_ip_limit = 'Sepcify IP address band that can access the admin page.';
$lang->admin_ip_allow = 'IP addresses allowed to log in as administrator';
$lang->admin_ip_deny = 'IP addresses forbidden to log in as administrator';
$lang->local_ip_address = 'Local IP address';
$lang->about_admin_ip_limit = 'Specify IP address which can access to admin page. Please note that only the specified IP addresses can access the admin page.';
$lang->about_admin_ip_allow = 'If this list is not empty, the administrator will only be able to log in from one of the listed IP addresses.';
$lang->about_admin_ip_deny = 'This list can be used to designate IP addresses that are not allowed to log in as administrator.';
$lang->msg_current_ip_will_be_denied = 'The given IP list cannot be applied, as they would block your own IP address.';
$lang->detail_about_ftp_info = 'FTP information is needed for easyinstall when save_mode = on.';
$lang->allow_use_favicon = 'Do you want to use favicon?';
$lang->about_use_favicon = 'You can upload 16x16 size<em>*.ico</em> file only.';
@ -208,4 +212,5 @@ $lang->sitelock_message = 'Sign Contents';
$lang->sitelock_message_help = 'You can use HTML tags.';
$lang->sitelock_warning_whitelist = 'You should include the IP of the administrator here.';
$lang->your_ip = 'Your IP';
$lang->sitelock_in_use = 'Site lock in use';
$lang->sitelock_in_use = 'This site is locked.';
$lang->about_sitelock_in_use = 'Only the administrator and visitors from specified IP addresses can access this site.';

View file

@ -1,5 +1,6 @@
<?php
$lang->admin = '管理者';
$lang->cmd_configure = '設定する';
$lang->subtitle_primary = '基本設定';
$lang->subtitle_security = 'セキュリティ設定';
$lang->subtitle_advanced = '上級設定';
@ -110,9 +111,12 @@ $lang->input_footer_script = '下段footerスクリプト';
$lang->detail_input_footer_script = '最下段にコードを追加します。管理者ページでは遂行できません。';
$lang->corp = '切り取り';
$lang->ratio = 'Ratio(縦横の比率をキープ)';
$lang->admin_ip_limit = '管理者のIPアドレス帯域';
$lang->admin_ip_allow = '管理者ログイン許容IP';
$lang->admin_ip_deny = '管理者ログイン禁止IP';
$lang->local_ip_address = 'ローカルIPアドレス';
$lang->about_admin_ip_limit = '該当IPについてのみ管理者ページへアクセスできるため、注意してください。IP帯域情報は、/files/config/db.config.php ファイルに保存されます。Change the line to multiple IP.';
$lang->about_admin_ip_allow = 'ここでIPアドレスの一覧を表示すると、そのIPのみ、管理者のログインが可能になります。すべてのIPからのログインを許可するには、リストを空白のままに。';
$lang->about_admin_ip_deny = 'ここに記載され、IPアドレスは、管理者のログインが禁止されます。';
$lang->msg_current_ip_will_be_denied = '入力された設定によると、現在ログインして、管理者のIPアドレスもブロックされます。再度確認してください。';
$lang->detail_about_ftp_info = 'FTP情報を入力すれば簡単設置を可能にします。FTP情報は files/config/ftp.config.php ファイルに保存されます。簡単設置ができない場合、PHPのsafe_modeをOnへ変更が必要です。';
$lang->allow_use_favicon = 'ファビコン設定';
$lang->about_use_favicon = '16 x 16 サイズの<em>*.ico</em> ファイルのみ登録できます。';
@ -197,3 +201,5 @@ $lang->sitelock_message = '案内文内容';
$lang->sitelock_message_help = 'HTMLタグを使用できます。';
$lang->sitelock_warning_whitelist = 'ここに管理者のIPを必ず記入てください。';
$lang->your_ip = '接続したIP';
$lang->sitelock_in_use = 'サイトロック状態です。';
$lang->about_sitelock_in_use = '管理者との接続が許可されたIPのみのサイトの利用が可能です。';

View file

@ -1,5 +1,6 @@
<?php
$lang->admin = '관리자';
$lang->cmd_configure = '설정하기';
$lang->subtitle_primary = '기본 설정';
$lang->subtitle_security = '보안 설정';
$lang->subtitle_advanced = '고급 설정';
@ -112,9 +113,12 @@ $lang->input_footer_script = '하단(footer) 스크립트';
$lang->detail_input_footer_script = '최하단에 코드를 삽입합니다. 관리자 페이지에서는 수행되지 않습니다.';
$lang->corp = 'Crop(잘라내기)';
$lang->ratio = 'Ratio(비율 맞추기)';
$lang->admin_ip_limit = '관리자 IP대역';
$lang->admin_ip_allow = '관리자 로그인 허용 IP';
$lang->admin_ip_deny = '관리자 로그인 금지 IP';
$lang->local_ip_address = '로컬 IP 주소';
$lang->about_admin_ip_limit = '관리자 페이지로 접근가능한 IP대역을 지정합니다. 해당 IP에 대해서만 관리자 페이지로 접근이 가능하므로 주의 바랍니다.';
$lang->about_admin_ip_allow = '여기에 IP 주소를 나열하면 해당 IP에서만 관리자 로그인이 가능하게 됩니다. 모든 IP에서 로그인을 허용하려면 목록을 비워 두십시오.';
$lang->about_admin_ip_deny = '여기에 나열된 IP 주소에서는 관리자 로그인이 금지됩니다.';
$lang->msg_current_ip_will_be_denied = '주어진 설정에 따르면 현재 로그인하신 관리자의 IP 주소도 차단됩니다. 다시 확인해 주십시오.';
$lang->detail_about_ftp_info = 'safe_mode = on 상태에서 쉬운설치를 사용하려면 FTP 정보를 입력해야 합니다.';
$lang->allow_use_favicon = '파비콘 지정';
$lang->about_use_favicon = '16 x 16 크기의<em>*.ico</em> 파일 업로드 권장.';
@ -205,5 +209,5 @@ $lang->sitelock_message = '안내문 내용';
$lang->sitelock_message_help = 'HTML 태그를 사용할 수 있습니다.';
$lang->sitelock_warning_whitelist = '사이트 잠금 사용시 관리자의 IP가 반드시 이 목록에 포함되어야 합니다.';
$lang->your_ip = '접속하신 IP';
$lang->sitelock_in_use = '사이트 잠금을 사용중입니다.';
$lang->about_sitelock_in_use = '관리자 페이지에서 허용한 IP를 제외한 사용자는 접속할 수 없습니다.';
$lang->sitelock_in_use = '사이트 잠금 상태입니다.';
$lang->about_sitelock_in_use = '관리자 및 접속이 허용된 IP에서만 사이트 이용이 가능합니다.';

View file

@ -93,7 +93,7 @@ $lang->input_footer_script = 'Alt (footer) Script Eklemek';
$lang->detail_input_footer_script = 'Script alt tarafa eklenmiştir. Yönetici sayfası çalışmıyor.';
$lang->corp = 'Crop (Kesme)';
$lang->ratio = 'Ratio(Yüzde Ayarı)';
$lang->admin_ip_limit = 'Yönetici sayfasına ulaşabileceğiniz IP aralığını belirleyiniz.';
$lang->admin_ip_allow = 'Yönetici sayfasına ulaşabileceğiniz IP aralığını belirleyiniz.';
$lang->local_ip_address = 'Yerel IP adresi';
$lang->about_admin_ip_limit = 'Sadece bu IP adresi üzerinden yönetici sayfasına erişim mümkündür.IP-bant bilgileri /files/config/db.config.php dosyasında saklanır. Satıra birden fazla öğe girin.';
$lang->detail_about_ftp_info = 'Kolay kurulum sağlayan FTP bilgilerini girdiğinizde. FTP bilgi, dosya / config / ftp.config.php dosyasında saklanır. Kolay yükleme sizin için mümkün değilse, PHP\'nin safe_mode ayarını On şeklinde değiştiriniz.';

View file

@ -92,7 +92,7 @@ $lang->input_footer_script = '输入页脚脚本';
$lang->detail_input_footer_script = '该脚本将被插入到页面的底部. 页面管理将无效.';
$lang->corp = '裁剪';
$lang->ratio = '缩放';
$lang->admin_ip_limit = '后台IP绑定';
$lang->admin_ip_allow = '后台IP绑定';
$lang->local_ip_address = '本地IP地址';
$lang->about_admin_ip_limit = '请注意只有绑定的IP才能访问后台。IP信息将保存在 /files/config/db.config.php. 每行一个IP。';
$lang->detail_about_ftp_info = '当设定FTP信息来启用快捷安装。FTP的信息保存在 /files/config/ftp.config.php. 如果不启用快捷安装请务必将开启PHP安全模式';

View file

@ -20,10 +20,17 @@
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="admin_allowed_ip">{$lang->admin_ip_limit} <a class="x_icon-question-sign" href="./common/manual/admin/#UMAN_config_general_admin_iplist" target="_blank">{$lang->help}</a></label>
<label class="x_control-label" for="admin_allowed_ip">{$lang->admin_ip_allow}</label>
<div class="x_controls">
<textarea name="admin_allowed_ip" id="admin_allowed_ip" rows="4" cols="42" placeholder="{$remote_addr} ({$lang->local_ip_address})" style="margin-right:10px">{$admin_allowed_ip}</textarea>
<p class="x_help-block">{$lang->about_ipaddress_input}</p>
<p class="x_help-block">{$lang->about_admin_ip_allow}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="admin_denied_ip">{$lang->admin_ip_deny}</label>
<div class="x_controls">
<textarea name="admin_denied_ip" id="admin_denied_ip" rows="4" cols="42" style="margin-right:10px">{$admin_denied_ip}</textarea>
<p class="x_help-block">{$lang->about_admin_ip_deny}</p>
</div>
</div>
<div class="x_clearfix btnArea">

View file

@ -12,7 +12,7 @@
<label class="x_control-label">{$lang->use_sitelock} <a class="x_icon-question-sign" href="./common/manual/admin/#UMAN_config_general_sitelock" target="_blank">{$lang->help}</a></label>
<div class="x_controls">
<label for="sitelock_locked_y" class="x_inline"><input type="radio" name="sitelock_locked" id="sitelock_locked_y" value="Y" checked="checked"|cond="$sitelock_locked" /> {$lang->cmd_yes}</label>
<label for="sitelock_locked_y" class="x_inline"><input type="radio" name="sitelock_locked" id="sitelock_locked_n" value="N" checked="checked"|cond="!$sitelock_locked" /> {$lang->cmd_no}</label>
<label for="sitelock_locked_n" class="x_inline"><input type="radio" name="sitelock_locked" id="sitelock_locked_n" value="N" checked="checked"|cond="!$sitelock_locked" /> {$lang->cmd_no}</label>
</div>
</div>

View file

@ -21,7 +21,7 @@
<div class="message error" cond="$db_info->use_sitelock == 'Y'">
<h2>{$lang->sitelock_in_use}</h2>
<p>{$lang->about_sitelock_in_use}</p>
<p>{$lang->about_sitelock_in_use} <a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigSitelock')}">{$lang->cmd_configure}</a></p>
</div>
<form action="./" method="post" class="message info x_clearfix" cond="!$isLicenseAgreement">

View file

@ -234,6 +234,16 @@ class installController extends install
}
}
// Apply site lock.
if (Context::get('use_sitelock') === 'Y')
{
$user_ip_range = getView('install')->detectUserIPRange();
Rhymix\Framework\Config::set('lock.locked', true);
Rhymix\Framework\Config::set('lock.message', 'This site is locked.');
Rhymix\Framework\Config::set('lock.allow', array('127.0.0.1', $user_ip_range));
}
// Save the new configuration.
Rhymix\Framework\Config::save();

View file

@ -153,9 +153,31 @@ class installView extends install
// Always use SSL if installing via SSL.
Context::set('use_ssl', RX_SSL ? 'always' : 'none');
Context::set('sitelock_ip_range', $this->detectUserIPRange());
$this->setTemplateFile('other_config');
}
/**
* Detect the IP range of the user.
*/
function detectUserIPRange()
{
if (RX_CLIENT_IP_VERSION === 4)
{
return preg_replace('/\.\d+$/', '.*', RX_CLIENT_IP);
}
elseif (function_exists('inet_pton'))
{
$binary = inet_pton(RX_CLIENT_IP);
$binary = substr($binary, 0, 8) . str_repeat(chr(0), 8);
return inet_ntop($binary) . '/64';
}
else
{
return RX_CLIENT_IP;
}
}
/**
* Detect best time zone for the user.
*/

View file

@ -77,9 +77,13 @@ $lang->about_nginx_rewrite = 'To use this feature at nginx, you need to configur
$lang->time_zone = 'Time Zone';
$lang->about_time_zone = 'If the server time is different from your time zone, you can use this option to display times in your time zone.';
$lang->use_ssl = 'SSL';
$lang->ssl_options['none'] = 'Never';
$lang->ssl_options['none'] = 'None';
$lang->ssl_options['optional'] = 'Optional';
$lang->ssl_options['always'] = 'Always';
$lang->use_sitelock = 'Site Lock';
$lang->sitelock_options['none'] = 'None';
$lang->sitelock_options['lock'] = 'Lock after Install';
$lang->about_sitelock_after_install = 'If you lock your site, only your current IP range (%s) will be able to access your site.';
$lang->about_database_file = 'Sqlite saves data in a file. Location of the database file should be unreachable by web<br/><span style="color:red">Data file should be inside the permission of 777.</span>';
$lang->success_installed = 'Installation has been completed.';
$lang->msg_db_checking = 'Checking...';

View file

@ -78,6 +78,10 @@ $lang->use_ssl = 'SSLを使用';
$lang->ssl_options['none'] = '使わない';
$lang->ssl_options['optional'] = '部分的に使う';
$lang->ssl_options['always'] = '常に使う';
$lang->use_sitelock = 'サイトロック';
$lang->sitelock_options['none'] = '使わない';
$lang->sitelock_options['lock'] = 'ロック';
$lang->about_sitelock_after_install = 'サイトをロックすると、インストールに使用したIP帯域%s以外接続できなくなるので注意してください。';
$lang->about_database_file = 'Sqliteはファイルにデータを保存します。そのため、データベースファイルにはウェブからアクセスできない場所にしなければなりません。<br/><span style="color:red">データファイルのパーミッションは「777」に設定してください。</span>';
$lang->success_installed = '正常にインストールされました。';
$lang->msg_cannot_proc = 'インストールできる環境が整っていないため、リクエストを実行できませんでした。';

View file

@ -80,6 +80,10 @@ $lang->use_ssl = 'SSL 사용';
$lang->ssl_options['none'] = '사용 안함';
$lang->ssl_options['optional'] = '선택적으로';
$lang->ssl_options['always'] = '항상 사용';
$lang->use_sitelock = '사이트 잠금';
$lang->sitelock_options['none'] = '사용 안함';
$lang->sitelock_options['lock'] = '잠금 상태로 설치';
$lang->about_sitelock_after_install = '사이트를 잠그면 설치에 사용하신 IP 대역 (%s) 외에는 접속할 수 없게 되니 주의하십시오.';
$lang->about_database_file = 'Sqlite는 파일에 데이터를 저장합니다. 데이터베이스 파일의 위치를 웹에서 접근할 수 없는 곳으로 해야 합니다.<br/><span style="color:red">데이터 파일은 777퍼미션 설정된 곳으로 지정해주세요.</span>';
$lang->success_installed = '설치가 되었습니다.';
$lang->msg_cannot_proc = '설치 환경이 갖춰지지 않아 요청을 실행할 수가 없습니다.';

View file

@ -176,7 +176,10 @@ button.grey:hover, a.button.grey:hover {
.x #content ul li {
line-height: 160%;
}
.x #content label input[type=checkbox] {
.x #content label.x_inline {
display: inline-block; margin-right: 12px;
}
.x #content label input[type=checkbox], .x #content label input[type=radio] {
position: relative;
top: 2px;
}
@ -207,6 +210,9 @@ button.grey:hover, a.button.grey:hover {
line-height: 17px; padding: 3px 5px;
width: 260px; box-sizing: border-box;
}
.x .x_control-group .x_controls input[type=checkbox], .x .x_control-group .x_controls input[type=radio] {
padding: 0; width: auto;
}
.x .x_control-group .x_controls select {
line-height: 17px; padding: 3px 7px 3px 3px;
width: 260px; box-sizing: border-box;

View file

@ -50,13 +50,19 @@
<div class="x_control-group">
<label class="x_control-label">{$lang->use_ssl}</label>
<div class="x_controls">
<select name="use_ssl">
<!--@foreach($lang->ssl_options as $key => $val)-->
<option value="{$key}" selected="selected"|cond="$use_ssl==$key" />{$val}</option>
<label for="ssl_{$key}" class="x_inline"><input type="radio" name="use_ssl" id="ssl_{$key}" value="{$key}" checked="checked"|cond="$use_ssl==$key" /> {$val}</label>
<!--@endforeach-->
</select>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->use_sitelock}</label>
<div class="x_controls">
<label for="sitelock_n" class="x_inline"><input type="radio" name="use_sitelock" id="sitelock_n" value="N" checked="checked" /> {$lang->sitelock_options['none']}</label>
<label for="sitelock_y" class="x_inline"><input type="radio" name="use_sitelock" id="sitelock_y" value="Y" /> {$lang->sitelock_options['lock']}</label>
</div>
</div>
<p class="install_help">{$lang->about_sitelock_after_install($sitelock_ip_range)}</p>
</div>
<div id="buttons">
<div class="align-left">

View file

@ -152,6 +152,7 @@ $lang->msg_accept_agreement = 'You have to accept the agreement.';
$lang->msg_user_denied = 'You have entered a prohibited ID.';
$lang->msg_user_not_confirmed = 'Your account is not activated yet. Please check your email.';
$lang->msg_user_limited = 'You have entered an ID that cannot be used before %s';
$lang->msg_admin_ip_not_allowed = 'Your IP address is not allowed to log in as an administrator.';
$lang->about_rechecked_password = 'Confirm your password before editing account information.';
$lang->about_user_id = 'User ID should be 3~20 characters long, consist of alphanumeric and start with a letter.';
$lang->about_password = 'Password should be 6~20 characters long.';

View file

@ -107,6 +107,7 @@ $lang->msg_accept_agreement = 'Usted primero debe aceptar el acuerdo';
$lang->msg_user_denied = 'ID ingresado ha sido prohibido para su uso';
$lang->msg_user_not_confirmed = '아직 메일 인증이 이루어지지 않았습니다. 메일을 확인해 주세요';
$lang->msg_user_limited = 'ID ingresado puede ser usado luego de %s';
$lang->msg_admin_ip_not_allowed = 'Su dirección IP no se puede iniciar la sesión como administrador.';
$lang->about_user_id = 'ID del usuario debe ser entre 3-20 letras que consiste en alfabetos+número con alfabeto como primera letra.';
$lang->about_password = 'Contraseña debe ser entre 6-20 letras';
$lang->about_user_name = 'Nombre debe ser entre 2-20 letras';

View file

@ -120,6 +120,7 @@ $lang->msg_accept_agreement = 'Vous devez agréer l\'accord';
$lang->msg_user_denied = 'Le compte que vous avez entré est suspendu';
$lang->msg_user_not_confirmed = 'Vous n\'avez pas encore authentifié. Verifiez votre mél, S.V.P.';
$lang->msg_user_limited = 'Vous avez entré un compte qui peut être utilisé depuis %s';
$lang->msg_admin_ip_not_allowed = 'Votre adresse IP ne soit pas autorisé à se connecter en tant qu\'administrateur.';
$lang->about_user_id = 'Le compte d\'utilisateur doit être long de 3~20 lettres et se composer des alphabets et des chiffres avec un alphabet au premier.';
$lang->about_password = 'Le Mot de Passe doit être long de 6~20 lettres.';
$lang->about_user_name = 'Le Nom doit être long de 2~20 lettres.';

View file

@ -156,6 +156,7 @@ $lang->msg_accept_agreement = '利用規約に同意しなければなりませ
$lang->msg_user_denied = '利用が中止されているユーザIDです。';
$lang->msg_user_not_confirmed = 'メールでの認証が行われていません。メールを確認してください。';
$lang->msg_user_limited = '入力したユーザIDは%s以前まで使用できません。';
$lang->msg_admin_ip_not_allowed = '接続したIPアドレスでは、管理者のログインが許容されないです。';
$lang->about_rechecked_password = '会員の情報を安全に保護するため、パスワードを再度確認します。';
$lang->about_user_id = 'ユーザーIDは、3~20文字までの英数文字にしてください。先頭文字は英字でなければなりません。';
$lang->about_password = 'パスワードは6~20文字にしてください。';

View file

@ -158,6 +158,7 @@ $lang->msg_accept_agreement = '약관에 동의해야 합니다.';
$lang->msg_user_denied = '입력한 아이디의 사용이 중지 되었습니다.';
$lang->msg_user_not_confirmed = '아직 메일 인증이 이루어지지 않았습니다. 메일을 확인해 주세요.';
$lang->msg_user_limited = '입력한 아이디는 %s 까지 사용하실 수 없습니다.';
$lang->msg_admin_ip_not_allowed = '접속하신 IP 주소에서는 관리자 로그인이 허용되지 않습니다.';
$lang->about_rechecked_password = '회원의 정보를 안전하게 보호하기 위해 비밀번호를 다시 한번 확인 합니다.';
$lang->about_user_id = '회원 ID는 3~20자 사이의 영문+숫자로 이루어져야 하며 영문으로 시작해야 합니다.';
$lang->about_password = '비밀번호는 6~20자로 되어야 합니다.';

View file

@ -154,6 +154,7 @@ $lang->msg_accept_agreement = '您必须同意条款。';
$lang->msg_user_denied = '您输入的用户名已禁止使用!';
$lang->msg_user_not_confirmed = '您的注册信息还没有被激活,请确认您的电子邮箱。';
$lang->msg_user_limited = '您输入的用户名%s以后才可以开始使用。';
$lang->msg_admin_ip_not_allowed = '连接的IP地址中,管理者的登录在不允许的。';
$lang->about_rechecked_password = '为了会员资料的安全保护,再次确认密码。';
$lang->about_user_id = '用户名长度必须由 3 ~20 字以内的英文+数字组成,且首个字母必须是英文字母。';
$lang->about_password = '密码长度必须在6~20字以内。';

View file

@ -142,6 +142,7 @@ $lang->msg_accept_agreement = '您必須同意條款。';
$lang->msg_user_denied = '您輸入的帳號已禁止使用!';
$lang->msg_user_not_confirmed = '您的註冊資料尚未啟用,請確認您的電子郵箱。';
$lang->msg_user_limited = '您輸入的帳號 %s 以後才可以開始使用。';
$lang->msg_admin_ip_not_allowed = '連接的IP地址中,管理者的登錄在不允許的。';
$lang->about_user_id = '帳號必須由 3~20 字以內的英文+數字組成,開頭必須是英文。';
$lang->about_password = '密碼必須在 6~20 字以內。';
$lang->about_user_name = '姓名必須是 2~20 字以內。';

View file

@ -294,12 +294,33 @@ class memberAdminModel extends member
*
* @return boolean (true : allowed, false : refuse)
*/
function getMemberAdminIPCheck()
function getMemberAdminIPCheck($allow_list = null, $deny_list = null)
{
$admin_ip_list = config('admin.allow');
if(!$admin_ip_list) return true;
if(!count($admin_ip_list) || IpFilter::filter($admin_ip_list)) return true;
else return false;
if ($allow_list = ($allow_list === null) ? config('admin.allow') : $allow_list)
{
foreach ($allow_list as $range)
{
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range))
{
return true;
}
}
return false;
}
if ($deny_list = ($deny_list === null) ? config('admin.deny') : $deny_list)
{
foreach ($deny_list as $range)
{
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range))
{
return false;
}
}
return true;
}
return true;
}
}
/* End of file member.admin.model.php */

View file

@ -1753,8 +1753,23 @@ class memberController extends member
}
return new Object(-1, ($this->memberInfo->refused_reason)? Context::getLang('msg_user_denied') . "\n" . $this->memberInfo->refused_reason : 'msg_user_denied');
}
// Notify if denied_date is less than the current time
if($this->memberInfo->limit_date && substr($this->memberInfo->limit_date,0,8) >= date("Ymd")) return new Object(-9,sprintf(Context::getLang('msg_user_limited'),zdate($this->memberInfo->limit_date,"Y-m-d")));
// Notify if user is limited
if($this->memberInfo->limit_date && substr($this->memberInfo->limit_date,0,8) >= date("Ymd"))
{
return new Object(-9,sprintf(Context::getLang('msg_user_limited'),zdate($this->memberInfo->limit_date,"Y-m-d")));
}
// Do not allow login as admin if not in allowed IP list
if($this->memberInfo->is_admin === 'Y' && $this->act === 'procMemberLogin')
{
$oMemberAdminModel = getAdminModel('member');
if(!$oMemberAdminModel->getMemberAdminIPCheck())
{
return new Object(-1, 'msg_admin_ip_not_allowed');
}
}
// Update the latest login time
$args->member_srl = $this->memberInfo->member_srl;
$output = executeQuery('member.updateLastLogin', $args);
@ -1819,17 +1834,8 @@ class memberController extends member
$autologin_output = executeQuery('member.insertAutologin', $autologin_args);
if($autologin_output->toBool()) setCookie('xeak',$autologin_args->autologin_key, $_SERVER['REQUEST_TIME']+31536000, '/');
}
if($this->memberInfo->is_admin == 'Y')
{
$oMemberAdminModel = getAdminModel('member');
if(!$oMemberAdminModel->getMemberAdminIPCheck())
{
$_SESSION['denied_admin'] = 'Y';
}
}
$this->setSessionInfo();
return $output;
}

View file

@ -1,3 +1,4 @@
<?php
$lang->message = '오류 표시';
$lang->about_skin = '오류 메시지용 스킨을 지정할 수 있습니다.';
$lang->msg_administrator_login = '관리자 로그인';

View file

@ -8,6 +8,7 @@
<div id="access">
<div class="login-header">
<h1><i class="icon-user"></i> {$system_message}</h1>
<div class="message" cond="defined('_XE_SITELOCK_MESSAGE_')">{constant('_XE_SITELOCK_MESSAGE_')}</div>
</div>
<div class="login-body">
<div cond="$XE_VALIDATOR_MESSAGE && $XE_VALIDATOR_ID == 'modules/message/skins/default/system_message/1'" class="message {$XE_VALIDATOR_MESSAGE_TYPE}">

View file

@ -1,49 +1,71 @@
@charset "utf-8";
@font-face{font-family:NG;src:url(https://themes.googleusercontent.com/static/fonts/earlyaccess/nanumgothic/v3/NanumGothic-Regular.eot);src:local(),url(https://themes.googleusercontent.com/static/fonts/earlyaccess/nanumgothic/v3/NanumGothic-Regular.woff) format('woff')}
body, #access table, #access input, #access textarea, #access select, #access button, #access label{font-family:,NanumGothic,NG,,Dotum,Arial,Helvetica,sans-serif;font-size:13px}
@media all and (max-width:980px){
body, #access table, #access input, #access textarea, #access select, #access button, #access label{font-family:,Dotum,Arial,Helvetica,sans-serif}
#access{
width: 360px;
margin: 40px auto;
background-color: #f9f9f9;
border: 1px solid #d8d8d8;
border-radius: 0;
box-shadow: none;
box-sizing: initial;
font-family: NanumBarunGothic, 'Malgun Gothic', Dotum, 'Apple SD Gothic Neo', AppleGothic, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
box-shadow: 0 2px 3px #eee;
}
@media screen and (max-width: 480px) {
#access { width: 300px; }
}
#access{width:400px;margin:30px auto;background-color:#ffffff;border:1px solid #999;border-radius:6px;box-shadow:0 3px 7px #ccc;box-sizing:border-box}
#access>.login-header{padding:9px 15px;border-bottom:1px solid #eee}
#access>.login-header>h1{margin:0;font-size:16px;line-height:1.4;font-weight:600;color:#666}
#access>.login-header>h1>i{opacity:.5;filter:alpha(opacity=50);margin:2px 0 0 0}
#access>.login-body{max-height:400px;padding:15px}
#access>.login-body>*:first-child{margin-top:0}
#access>.login-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff; color:#ccc}
#access .control-group{position:relative;padding:0 14px 0 0;margin:0;clear:both}
#access .control-group:before{content:"";display:block;clear:both}
#access form{margin:0}
#access fieldset{margin:0}
#access label{cursor:pointer;display:inline-block}
#access input[type="text"],
#access input[type="email"],
#access input[type="password"]{width:100%}
#access input[type="checkbox"]{margin:0}
#access .btn{border-radius:2px;overflow:visible;font-size:14px;line-height:18px;height:auto;padding:3px 9px;text-decoration:none}
#access .submit{position:absolute;top:0;right:0}
#access p{margin:10px 0}
#access #warning{margin-right:-14px}
#access a{color: #0088cc;text-decoration: none;}
/* Message customize */
#access{width:380px;background-color:#f9f9f9;border:1px solid #d8d8d8;border-radius:0;box-shadow:none;box-sizing:initial;font-family:'나눔바른고딕',NanumBarunGothic,ng,'맑은 고딕','Malgun Gothic','돋움',Dotum,'애플 SD 산돌고딕 Neo','Apple SD Gothic Neo',AppleGothic,Helvetica,sans-serif;-webkit-font-smoothing:antialiased;box-shadow: 0 2px 3px #eee}
#access>.login-header{padding:30px 30px 24px;border-bottom:0}
#access>.login-header p{margin:0 0 50px;font-size:40px;font-weight:normal;color:#444;line-height:50px;font-family:Raleway,'나눔바른고딕',NanumBarunGothic,ng,AppleGothic,Helvetica,sans-serif}
#access>.login-header h1{margin:10px 0;font-size:16px;line-height:20px;color:#f44336;font-weight:normal}
#access>.login-body{padding:20px 30px 0}
#access>.login-body a{display:block;height:54px;background-color:#444;font-size:16px;line-height:54px;text-align:center;color:#eee}
#access>.login-body a:hover,
#access>.login-body a:focus,
#access>.login-body a:active{background-color:#333}
#access>.login-footer{padding:13px 0 42px;margin:30px 30px 0;text-align:center;border-top:0;border-radius:0;box-shadow:none;background:none;color:#888}
#access>.login-footer>a{text-decoration:none;font-size:13px;color:#aaa}
#access>.login-footer>a:hover,
#access>.login-footer>a:active,
#access>.login-footer>a:focus{text-decoration:underline}
#access>.login-footer>.bar{display:inline-block;margin:0 8px;color:#aaa}
#access .login-header { margin: 30px; padding: 0; }
#access .login-header h1 { margin: 0; padding: 0; font-size: 16px; line-height: 20px; color:#f44336; font-weight:normal; }
#access .login-header .message { border: 0; border-radius: 0; padding: 0; margin: 20px 0 0 0; }
@media all and (max-width:480px){
#access{position:static;margin:0;width:100%}
}
#access .login-body { margin: 30px; padding: 0; }
#access .login-body p { margin: 0; padding: 0; }
#access .login-body form { margin: 0; padding: 0; }
#access input[type=text], #access input[type=email], #access input[type=password] {
font-family: Arial, NanumBarunGothic, 'Malgun Gothic', Dotum, 'Apple SD Gothic Neo', AppleGothic, sans-serif;
font-size: 13px;
line-height: 17px;
width: 100%;
box-sizing: border-box;
border: 1px solid #aaa;
background: #fff;
padding: 6px;
margin-bottom: 8px;
}
#access input[type=checkbox] { margin: 0 4px 0 0; position: relative; top: 2px; }
#access .login-body .control-group { margin-bottom: 16px; }
#access .login-body a.button {
line-height: 54px;
}
#access .login-body .button {
display: block;
width: 100%;
height: 54px;
border: 0;
background-color: #444;
font-size: 16px;
font-family: inherit;
text-align: center;
text-decoration: none;
color: #eee;
}
#access .login-body .button:hover,
#access .login-body .button:focus,
#access .login-body .button:active {
background-color: #222;
}
#access .login-footer {
margin: 30px;
text-align: center;
border-top: 0;
color: #888;
}
#access .login-footer>a{text-decoration:none;font-size:13px;color:#888}
#access .login-footer>a:hover,
#access .login-footer>a:active,
#access .login-footer>a:focus{text-decoration:underline}
#access .login-footer>.bar{display:inline-block;margin:0 8px;color:#aaa}

View file

@ -1,20 +1,52 @@
<!--// META -->
{Context::addHtmlHeader('<meta name="viewport" content="width=1240">')}
<!--// CSS -->
<load target="./css/message.css" />
<!--// BODY -->
<div id="access">
<div class="login-header">
<p>OOPS!</p>
<h1>{$system_message}</h1>
<div class="message" cond="defined('_XE_SITELOCK_MESSAGE_')">{constant('_XE_SITELOCK_MESSAGE_')}</div>
</div>
<div class="login-body">
<p><a href="{getUrl('', 'act', 'dispMemberLoginForm')}">{$lang->cmd_login}</a></p>
<div cond="!$is_logged">
<div class="login-body">
<div cond="$XE_VALIDATOR_MESSAGE && $XE_VALIDATOR_ID == 'modules/message/skins/default/system_message/1'" class="message {$XE_VALIDATOR_MESSAGE_TYPE}">
<p>{$XE_VALIDATOR_MESSAGE}</p>
</div>
<form id="message_login_form" ruleset="@login" action="{getUrl('','act','procMemberLogin')}" method="post">
<input type="hidden" name="module" value="member" />
<input type="hidden" name="act" value="procMemberLogin" />
<input type="hidden" name="success_return_url" value="{getRequestUriByServerEnviroment()}" />
<input type="hidden" name="xe_validator_id" value="modules/message/skins/default/system_message/1" />
<div class="control-group">
<input type="text" name="user_id" id="uid" title="{$lang->user_id}" placeholder="{$lang->user_id}" required autofocus cond="$member_config->identifier != 'email_address'" />
<input type="email" name="user_id" id="uid" title="{$lang->email_address}" placeholder="{$lang->email_address}" required autofocus cond="$member_config->identifier == 'email_address'" />
<input type="password" name="password" id="upw" title="{$lang->password}" placeholder="{$lang->password}" required />
<label for="keepid" cond="!defined('_XE_SITELOCK_MESSAGE_')">
<input type="checkbox" name="keep_signed" id="keepid" class="inputCheck" value="Y" onclick="jQuery('#warning')[(jQuery('#keepid:checked').size()&gt;0?'addClass':'removeClass')]('open');" />
{$lang->keep_signed}
</label>
</div>
<p><button type="submit" class="button" href="#" onclick="$('#message_login_form').submit()">
<!--@if(defined('_XE_SITELOCK_MESSAGE_'))-->
{$lang->msg_administrator_login}
<!--@else-->
{$lang->cmd_login}
<!--@end-->
</button></p>
</form>
</div>
<div class="login-footer" cond="!defined('_XE_SITELOCK_MESSAGE_')">
<a href="{getUrl('', 'act', 'dispMemberFindAccount')}">{$lang->cmd_find_member_account}</a>
<span class="bar">|</span>
<a href="{getUrl('', 'act', 'dispMemberSignUpForm')}">{$lang->cmd_signup}</a>
</div>
</div>
<div class="login-footer">
<a href="{getUrl('', 'act', 'dispMemberFindAccount')}">{$lang->cmd_find_member_account}</a>
<span class="bar">|</span>
<a href="{getUrl('', 'act', 'dispMemberSignUpForm')}">{$lang->cmd_signup}</a>
<div cond="$is_logged">
<div class="login-body">
<p><a class="button" href="{getUrl()}">{$lang->cmd_back}</a></p>
</div>
<div class="login-footer">
<a href="{getUrl('', 'act', 'dispMemberLogout')}">{$lang->cmd_logout}</a>
</div>
</div>
</div>