This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/attachment.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* 通用附件关联处理;
*/
class filterAttachment extends Controller{
function __construct(){
parent::__construct();
}
// 自动绑定处理;
public function bind(){
Hook::bind('admin.notice.add.after',array($this,'doNoticeLink'));
Hook::bind('admin.notice.edit.after',array($this,'doNoticeLinkEdit'));
Hook::bind('admin.notice.remove.after',array($this,'doNoticeClear'));
Hook::bind('comment.index.add.after',array($this,'doCommentLink'));
Hook::bind('comment.index.edit.after',array($this,'doNoticeLinkEdit'));
Hook::bind('comment.index.remove.after',array($this,'doCommentClear'));
}
public function doNoticeLink($json){Action('explorer.attachment')->noticeLink($json['info']);}
public function doNoticeLinkEdit($json){Action('explorer.attachment')->noticeLink($this->in['id']);}
public function doNoticeClear($json){Action('explorer.attachment')->noticeClear($this->in['id']);}
public function doCommentLink($json){Action('explorer.attachment')->commentLink($json['data']);}
public function doCommentClear($json){Action('explorer.attachment')->commentClear($this->in['id']);}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/fileOut.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* fileOut数据过滤; fileOutBy 相对路径处理:js-import; css-import.src处理;
*/
class filterFileOut extends Controller{
function __construct(){
parent::__construct();
}
// 自动绑定处理;
public function bind(){
$action = strtolower(ACTION);
$disableCookie = array(
'explorer.index.fileoutby',
'explorer.share.fileoutby',
'explorer.index.filedownload',
'explorer.index.fileout',
'explorer.share.filedownload',
'explorer.share.fileout',
);
$allowViewToken = array(
'explorer.index.fileoutby',
'explorer.share.fileoutby',
);
if(in_array($action,$disableCookie)){
Cookie::disable(true);allowCROS();
}
// 仅限当前ip使用的token,有效期1天(该token仅限文件相对路径获取使用)
if(in_array($action,$allowViewToken)){
$token = isset($_REQUEST['viewToken']) ? $_REQUEST['viewToken']:'';
if($token && strlen($token) < 500){
$pass = substr(md5('safe_'.get_client_ip().Model('SystemOption')->get('systemPassword')),0,15);
$sessionSign = Mcrypt::decode($token,$pass);
if($sessionSign){Session::sign($sessionSign);}
$parse = kodIO::parse($this->in['path']);// 不允许以相对路径获取php扩展名文件;避免管理员被钓鱼攻击;
$pathAdd = kodIO::pathUrlClear(rawurldecode($this->in['add']));
$distPath = kodIO::pathTrue($parse['path'].'/../'.$pathAdd);
if(get_path_ext($distPath) == 'php'){show_json('not allow',false);}
// header("Status: 404 Not Found [viewToken]");exit;
}
Hook::bind('PathDriverBase.fileOut.before',array($this,'fileOut'));
}
}
public function fileOut($file,$fileSize,$filename,$ext){
// write_log(REQUEST_METHOD.":".$filename.';'.$file,'test');
if(!isset($this->in['replaceType']) || !$this->in['replaceType']){
if($ext != 'js'){return;}
$this->outputJs(IO::getContent($file));
return;
}
if(!$filename || $fileSize >= 10*1024*1024 || !in_array($ext,array('css','js')) ){return;}
$content = IO::getContent($file);
if($ext == 'css' && $this->in['replaceType'] == 'css-import'){$this->cssParse($content);}
if($ext == 'js' && $this->in['replaceType'] == 'script-import'){$this->scriptParse($content);}
if($ext == 'js' && $this->in['replaceType'] == 'script-wasm'){$this->scriptParseWasm($content);}
}
private function cssParse($content){
$self = $this;
$content = preg_replace_callback("/url\s*\(\s*['\"]*(.*?)['\"]*\s*\)/",function($matchs) use($self){
return 'url("'.$self->urlFilter($matchs[1]).'")';
},$content);
$content = preg_replace_callback("/@import\s+['\"](.*\.css)['\"]/u",function($matchs) use($self){
return '@import "'.$self->urlFilter($matchs[1]).'"';
},$content);
$this->output($content);
}
private function scriptParse($content){
$self = $this;$contentOld = $content;
$content = preg_replace_callback("/importScripts\s*\(\s*([`'\"].*?[`'\"])\s*\)/",function($matchs) use($self){
$char = substr($matchs[1],0,1);$url = substr($matchs[1],1,strlen($matchs[1])-2);
return 'importScripts('.$char.$self->urlFilter($url).$char.')';
},$content);
$content = preg_replace_callback("/\s+from\s+([`'\"].*?['\"`])/",function($matchs) use($self){
$char = substr($matchs[1],0,1);$url = substr($matchs[1],1,strlen($matchs[1])-2);
return ' from '.$char.$self->urlFilter($url).$char;
},$content);
$content = preg_replace_callback("/import\s+(['\"`].*?['\"`])/",function($matchs) use($self){
$char = substr($matchs[1],0,1);$url = substr($matchs[1],1,strlen($matchs[1])-2);
return 'import '.$char.$self->urlFilter($url).$char;
},$content);
// await import( `./Sidebar.Geometry.${ geometry.type }.js` ); 该情况兼容;
$content = preg_replace_callback("/import\s*\(\s*(['\"`].*?['\"`])\s*\)/",function($matchs) use($self){
$char = substr($matchs[1],0,1);$url = substr($matchs[1],1,strlen($matchs[1])-2);
return 'import('.$char.$self->urlFilter($url).$char.')';
},$content);
// var_dump($contentOld,$content);exit;
$this->outputJs($content);
}
private function outputJs($content){
// window.location处理; 统一替换为_location_;解决跨域问题;
$locationHas = "(hash|host|hostname|href|orgin|pathname|port|protocol|reload|replace|search)";
$content = preg_replace("/(^|[^\w_.])window\.location($|[^\w_])/","$1window._location_$2",$content);
$content = preg_replace("/(^|[^\w_.])location\.".$locationHas."($|[^\w_])/","$1_location_.$2$3",$content);
$this->output($content);
}
private function scriptParseWasm($content){
$self = $this;
$content = preg_replace_callback("/=\s*\"([\w\.\-\_]+\.wasm)\"/",function($matchs) use($self){
return '="'.$self->urlFilter($matchs[1]).'"';
},$content);
$this->output($content);
}
private function urlFilter($url){
if(substr($url,0,5) == 'http:' || substr($url,0,6) == 'https:'){return $url;} // 外部链接;
$url = kodIO::pathUrlClear($url);
$path = rawurldecode($_GET['path']);// $this->in['path'], 外链分享时可能被替换;
// 采用相对路径重新计算; 确保多个位置import 最后引用路径一致;
// 路径有变化时js多个地方import同一个文件,但url路径不一致,时会导致重复执行;
$addNew = kodIO::pathTrue(kodIO::pathUrlClear($this->in['add']).'/../'.$url);
// 路径中不替换部分; 兼容js中url字符串带`${v}`变量情况;
$addNew = str_replace(array('%24','%20','%7B','%7D'),array('$',' ','{','}'),rawurlencode($addNew));
$shareID = isset($this->in['shareID']) ? '&shareID='.$this->in['shareID']:'';
$param = '&viewToken='.$this->in['viewToken'].$shareID.'&replaceType='.$this->in['replaceType'];
$url = APP_HOST.'index.php?'.str_replace('.','/',ACTION); // chrome xhr跨域options目录预检处理;需要带上index.php
return $url.$param.'&path='.rawurlencode($path).'&add='.$addNew;
}
private function output($content){
header('HTTP/1.1 200 OK');
header('Content-Encoding: none');
header('Content-Length:'.strlen($content));
echo $content;exit;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/html.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* 通用数据过滤;
*/
class filterHtml extends Controller{
function __construct(){
parent::__construct();
}
// 自动绑定处理;
public function bind(){
$action = strtolower(ACTION);
$check = array(
'admin.notice.add' => array('content'=>'htmlFilter'),
'admin.notice.edit' => array('content'=>'htmlFilter'),
'comment.index.add' => array('content'=>'htmlOnlyImage','title'=>'htmlClear')
);
if(!isset($check[$action])) return;
$this->filter($check[$action]);
}
public function filter($item){
$in = $this->in;
foreach ($item as $key => $method) {
if(!isset($in[$key]) || !$in[$key]) continue;
switch($method){
case 'htmlFilter':$this->in[$key] = Html::clean($in[$key]);break;
case 'htmlOnlyImage':$this->in[$key] = Html::onlyImage($in[$key]);break;
case 'htmlClear':$this->in[$key] = clear_html($in[$key]);break;
case 'json':$this->in[$key] = json_decode($in[$key]);break;
default:break;
}
}
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/index.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* 动作hook;
*
* 注意: 事件绑定需要在事件触发之前;(之后会造成触发时没有回调情况)
* 1. 插件注册bind事件: 在pluginModel初始化之后依次绑定 eg:LDAP @ user.index.loginsubmit.before
* 2. 插件中trigger的事件: 需要在pluginModel之前绑定好; eg:webdav@ user.index.userInfo)
*/
class filterIndex extends Controller{
function __construct() {
parent::__construct();
}
public function bindBefore(){
Action("filter.fileOut")->bind();
}
public function bind(){
Action("filter.userRequest")->bind();
Action("filter.userCheck")->bind();
Action("filter.userLoginState")->bind();
Action("filter.attachment")->bind();
Action("filter.html")->bind();
Action("filter.template")->bind();
}
public function trigger(){
Action("filter.post")->check();
Action("filter.userGroup")->check();
Action("filter.limit")->check();
Action("explorer.seo")->check();
Hook::trigger(strtolower(ACTION).'.before',array());
Hook::bind('show_json',array($this,'eventAfter'));
}
public function eventAfter($data){
if(!$data['code']) return $data;
$action = strtolower(ACTION).'.after';
if($action == 'user.view.options.after') return $data;//手动调用过
$returnData = Hook::trigger($action,$data);
return is_array($returnData) ? $returnData:$data;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/limit.class.php'
<?php
class filterLimit extends Controller{
function __construct(){
parent::__construct();
}
public function check(){
$action = strtolower(ACTION);
switch($action) {
case 'explorer.index.pathcopyto':$this->checkShareCopy();break;
case 'explorer.usershare.edit':$this->checkShareLink();break;
case 'explorer.index.unzip':$this->checkUnzip();break;
case 'explorer.index.zip':
case 'explorer.index.zipdownload':$this->checkZip();break;
case 'explorer.share.fileupload':
case 'explorer.upload.fileupload':$this->checkUpload();break;
default: break;
}
}
/**
* 分享转存个数限制
* @return void
*/
private function checkShareCopy(){
$list = json_decode($this->in['dataArr'],true);
$list = is_array($list) ? $list : array();
$storeMax = intval($this->config['settings']['storeFileNumberMax']);
if(!$storeMax) return;
for ($i=0; $i < count($list); $i++) {
$path = $list[$i]['path'];
$pathParse= KodIO::parse($path);
if($pathParse['type'] != KodIO::KOD_SHARE_LINK) continue;
if(!$info = Action('explorer.share')->sharePathInfo($path)){
show_json($GLOBALS['explorer.sharePathInfo.error'], false);
}
if($info['type'] == 'folder' && $storeMax && $info['children']['fileNum'] > $storeMax){
show_json(LNG('explorer.filter.shareCopyLimit').$storeMax, false);
}
}
}
/**
* 分享文件/夹大小限制
* @return void
*/
private function checkShareLink(){
$data = Input::getArray(array(
"shareID" => array("check"=>"int"),
"isLink" => array("check"=>"bool", "default"=>0),
));
if($data['isLink'] == 1){
$shareMax = floatval($this->config['settings']['shareLinkSizeMax'])*1024*1024*1024;
$shareInfo = Model('Share')->getInfo($data['shareID']);
if($shareMax && floatval($shareInfo['sourceInfo']['size']) > $shareMax){
$sizeShow = size_format($shareMax);
show_json(LNG('explorer.filter.shareSizeLimit').$sizeShow, false);
}
}
}
/**
* 解压缩大小限制
* @return void
*/
private function checkUnzip(){
$path = Input::get('path', 'require');
$info = IO::info($path);
if(!$info) return;
$unzipMax = floatval($this->config['settings']['unzipFileSizeMax'])*1024*1024*1024;
if($unzipMax && floatval($info['size']) > $unzipMax){
$sizeShow = size_format($unzipMax);
show_json(LNG('explorer.filter.unzipSizeLimit').$sizeShow, false);
}
}
/**
* 压缩大小限制
* @return void
*/
private function checkZip(){
$list = json_decode($this->in['dataArr'],true);
$size = 0;
foreach($list as $item) {
$info = IO::infoSimple($item['path']);
$size += (float) $info['size'];
}
$zipMax = floatval($this->config['settings']['zipFileSizeMax'])*1024*1024*1024;
if($zipMax && $size > $zipMax){
$sizeShow = size_format($zipMax);
show_json(LNG('explorer.filter.zipSizeLimit').$sizeShow, false);
}
}
/**
* 上传大小限制
* @return void
*/
private function checkUpload(){
$size = Input::get('size', null, 0);
$uploadMax = floatval($this->config['settings']['ignoreFileSize'])*1024*1024*1024;
if($size && $uploadMax && floatval($size) > $uploadMax){
$sizeShow = size_format($uploadMax);
show_json(LNG('explorer.filter.uploadSizeLimit').$sizeShow, false);
}
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/post.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* method过滤;
*
* csrf防护:(外部js或内部链接构造越权接口请求)
* 1. refer白名单;
* 2. 一律post请求; get允许的控制器白名单; 插件处理;
* 3. hash校验;(UA不为app和pc客户端);
*/
class filterPost extends Controller{
function __construct(){
parent::__construct();
}
// 一律post请求; get请求白名单; 插件处理;
public function check(){
if( Model('SystemOption')->get('csrfProtect') != '1') return;
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
if( strstr($ua,'kodbox') ||
strstr($ua,'okhttp') ||
strstr($ua,'kodcloud')
){return;}
// if(GLOBAL_DEBUG) return;
$theMod = strtolower(MOD);
$theST = strtolower(ST);
$theACT = strtolower(ACT);
$theAction = strtolower(ACTION);
$GLOBALS['config']['jsonpAllow'] = false; //全局禁用jsonp
if($theMod == 'plugin'){
$GLOBALS['config']['jsonpAllow'] = true;
return; //插件内部自行处理;
}
// webdav 挂载kod; 当前开启了csrf防护,直接接口上传时不处理;
if($theACT == 'fileupload' || $theMod.'.'.$theST == 'explorer.shareout'){
allowCROS();// 允许跨域访问;外部联合分享, 前端直传;
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS'){exit;}
if(isset($_POST['clientFrom']) && $_POST['clientFrom'] =='webdav-kodbox'){return;}
}
$allowGetArr = array(
'explorer.fileview' => 'index',
'explorer.history' => 'fileOut',
'explorer.index' => 'fileOut,fileDownload,fileOutBy,fileDownloadRemove',
'explorer.share' => 'fileOut,fileDownload,fileOutBy,zipDownload,fileDownloadRemove,file',
'admin.setting' => 'get,server',
'admin.repair' => '*',
'install.index' => '*',
'user.index' => 'index,autoLogin,loginSubmit,logout',//accessTokenGet logout
'user.view' => '*',
'user.sso' => '*',
'test' => '*',
'sitemap' => '*',
);
$allowGet = false;
$ST_MOD = $theMod.'.'.$theST;
if(isset($allowGetArr[$ST_MOD])){
$methods = strtolower($allowGetArr[$ST_MOD]);
$methodArr = explode(',',$methods);
if($methods == '*' || in_array($theACT,$methodArr)){
$allowGet = true;
}
}
if(isset($allowGetArr[MOD])){$allowGet = true;}
//必须使用POST的请求:统一检测csrfToken;
if($allowGet) return;
// 无需登录的接口不处理;
$authNotNeedLogin = $this->config['authNotNeedLogin'];
foreach ($authNotNeedLogin as &$val) {
$val = strtolower($val);
};unset($val);
if(in_array($theAction,$authNotNeedLogin)) return;
foreach ($authNotNeedLogin as $value) {
$item = explode('.',$value); //MOD,ST,ACT
if( count($item) == 2 &&
$item[0] === $theMod && $item[1] === '*'){
$allowGet = true;break;
}
if( count($item) == 3 &&
$item[0] === $theMod && $item[1] === $theST &&$item[2] === '*'){
$allowGet = true;break;
}
}
if($allowGet) return;
$this->checkCsrfToken();
if(REQUEST_METHOD != 'POST'){
// csrf校验后, post不再是必须的;
// show_json('REQUEST_METHOD must be POST!',false);
}
}
// csrfToken检测; 允许UA为APP,PC客户端的情况;
private function checkCsrfToken(){
if(isset($_REQUEST['accessToken'])) return;
if(!$this->in['CSRF_TOKEN'] || $this->in['CSRF_TOKEN'] != Cookie::get('CSRF_TOKEN')){
$className = substr(ACTION,0,strrpos(ACTION,'.'));
if(!Action($className)){header('HTTP/1.1 404 Not Found');exit;}
//write_log(array('CSRF_TOKEN error',$this->in,$_COOKIE,$_SERVER['HTTP_USER_AGENT']),'error');
Cookie::remove('CSRF_TOKEN');// 部分手机浏览器异常情况(ios-夸克浏览器: 打开zip内视频,关闭后拉取文件列表)
return show_json('CSRF_TOKEN error!',false);
}
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/template.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* 通用数据过滤;
*/
class filterTemplate extends Controller{
function __construct(){
parent::__construct();
}
// 自动绑定处理;
public function bind(){
Hook::bind('user.index.index',array($this,'lessCompile'));
}
/**
less 自动编译处理; 缓存处理; 5s编译;
*/
public function lessCompile(){
if(!STATIC_DEV) return;
if(!$this->lessChange()) return;
Action("test.debug")->less(false);
}
private function lessChange(){
$path = BASIC_PATH.'static/style/skin_dev';
$list = IO::listAll($path);
$str = '';
foreach ($list as $item) {
$str .= $item['path'].';'.$item['modifyTime'].';'.$item['size'];
}
$hashNew = md5($str);
$hashBefore = Cache::get('debug.lessCompile.key');
if($hashNew == $hashBefore) return false;
Cache::set('debug.lessCompile.key',$hashNew);
return true;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/userCheck.class.php'
<?php
/**
* 用户登录检测
*
* 密码错误次数处理;
* 登录ip白名单处理; 只检验拦截登录接口;
*/
class filterUserCheck extends Controller {
function __construct() {
parent::__construct();
}
public function bind(){
$this->options = Model('systemOption')->get();
if(isset($this->in['HTTP_X_PLATFORM_1'])){ // 新版APP端,对此参数做了编码处理;
$this->in['HTTP_X_PLATFORM'] = base64_decode($this->in['HTTP_X_PLATFORM_1']);
$_REQUEST['HTTP_X_PLATFORM'] = $this->in['HTTP_X_PLATFORM'];
}
$this->ipCheck();
$this->setAppLang();
Hook::bind('user.index.loginSubmitBefore',array($this,'loginSubmitBefore'));
Hook::bind('user.index.loginBefore',array($this,'loginBefore'));
}
public function loginBefore($user){
return $this->userLoginCheck($user);
}
// 密码输入错误记录锁定;
public function loginSubmitBefore($name,$user){
return $this->userLoginLockCheck($name,$user);
}
/**
* 密码强度校验;
*
* none:不限制,默认;
* strong: 中等强度, 长度大于6; 必须同时包含英文和数字;
* strongMore: 高强度, 长度大于8; 必须同时包含数字,大写英文,小写英文; =>[数字、大、小写字母、特殊字符]至少含3种
*
* 检测点: 用户注册;用户修改密码;管理员添加用户;管理员修改用户;导入用户;
* 前端点: 登录成功后:如果密码规则不匹配当前强度,则提示修改密码;[提示点:注册密码,修改密码,编辑用户设置密码,添加用户设置密码]
*/
public function password($password,$out=false){
$type = $this->options['passwordRule'];
if( !$type || $type == 'none') return true;
$length = strlen($password);
$hasNumber = preg_match('/\d/',$password);
$hasChar = preg_match('/[A-Za-z]/',$password);
$hasCharBig = preg_match('/[A-Z]/',$password);
$hasCharSmall = preg_match('/[a-z]/',$password);
$hasCharOthers = preg_match('/[~!@#$%^&*]/',$password);
if( $type == 'strong' && $length >= 6 && $hasNumber && $hasChar){
return true;
}else if( $type == 'strongMore' && $length >= 8){
$classCnt = 0;
if($hasNumber) $classCnt++;
if($hasCharBig) $classCnt++;
if($hasCharSmall) $classCnt++;
if($hasCharOthers) $classCnt++;
if ($classCnt >= 3) return true;
}
return false;
}
public function passwordTips(){
$type = $this->options['passwordRule'];
$desc = array(
'strong' => LNG('admin.setting.passwordRuleStrongDesc'),
'strongMore' => LNG('admin.setting.passwordRuleStrongMoreDesc')
);
$error = LNG('user.passwordCheckError');
$errorMore = isset($desc[$type]) ? ";<br/>".$desc[$type]:'';
return show_json($error.$errorMore,false);
}
/**
* ip黑名单限制处理;
* 仅判断:IP+设备 (仅限所有人时情况,指定用户时忽略,在登录时判断);
*/
private function ipCheck(){
$this->_checkConfig();
if(_get($this->config,'loginIpCheckIgnore') == '1') return true;// 手动关闭ip白名单检测;
if(!_get($this->options,'loginCheckAllow')) return true;
$ip = get_client_ip();
$serverIP = get_server_ip();
$device = $this->getDevice();
$checkList = json_decode($this->options['loginCheckAllow'],true);
if(!$checkList) return true;
if($ip == 'unknown' || $ip == $serverIP || $ip == '127.0.0.1') return true;
foreach ($checkList as $item){
if($item['loginIpCheck'] != '2') continue;
$userSelect = json_decode($item['userSelect'],true);
if(! isset($userSelect['all']) || $userSelect['all'] == '0') continue;
$allowDevice = $this->checkDevice($device,$item['device']);
if( $allowDevice && $this->checkIP($ip,$item['disableIp']) ){
$error = UserModel::errorLang(UserModel::ERROR_IP_NOT_ALLOW);
if($device['type'] == 'app'){
show_json($error."; IP: ".$ip,false);
}else{
show_tips($error."<br>IP: ".$ip);
}
exit;
}
}
return true;
}
// app多语言自动识别处理;
private function setAppLang(){
$ua = $_SERVER['HTTP_USER_AGENT'].';';
if(!strstr($ua,'kodCloud-System:iOS')) return;
$lang = match_text($ua,"Language:(.*);");
$langMap = array('zh-Hans'=>'zh-CN','zh-Hant'=>'zh-TW');
$setLang = $langMap[$lang] ? $langMap[$lang] : $lang;
$GLOBALS['config']['settings']['language'] = $setLang;
}
/**
* 用户登录限制管控
*
* 可以配置多个: 用户/部门/权限组 + 设备类型 + ip白名单; 组合的限制策略;
* 根据规则列表顺序依次进行过滤; 所有都能通过才算放过;
*/
private function userLoginCheck($user){
if($this->config['loginIpCheckIgnore'] == '1') return true;// 手动关闭ip白名单检测;
if(!$this->options['loginCheckAllow']) return true;
$ip = get_client_ip();
$serverIP = get_server_ip();
$device = $this->getDevice();
$checkList = json_decode($this->options['loginCheckAllow'],true);
if(!$checkList) return true;
// if($ip == 'unknown' || $ip == $serverIP || $ip == '127.0.0.1') return true;
$error = UserModel::ERROR_IP_NOT_ALLOW;// 您当前ip不在允许登录的ip白名单里,请联系管理员!;
$rootIpAdd = "
10.0.0.0-10.255.255.255
192.168.0.0-192.168.255.255";
foreach ($checkList as $item){
if(!Action('user.authPlugin')->checkAuthValue($item['userSelect'],$user)) continue;
$allowIp = true;
$allowDevice = $this->checkDevice($device,$item['device']);
if($item['loginIpCheck'] == '1'){
// 系统管理员允许内网登录
$role = Model('SystemRole')->listData($user['roleID']);
if($role['administrator'] == '1'){
$item['loginIpAllow'] .= $rootIpAdd;
}
$allowIp = $this->checkIP($ip,$item['loginIpAllow']);
}else if($item['loginIpCheck'] == '2'){
// 限制ip黑名单, 当前ip符合时, 设备符合指定设备则不允许登录;
if( $this->checkIP($ip,$item['disableIp']) ){
return $allowDevice ? $error:true;
}
}
return ($allowDevice && $allowIp) ? true:$error;
// 检测所有规则, 规则包含当前用户,不符合则不允许, 所有都通过才算通过;
// if(!$allowDevice || !$allowIp) return $error;
}
return true;
}
/**
* ip检测支持规则; 逗号或换行隔开多个规则;
*
* 单行为ip: 相等则匹配
* 单行为ip前缀: ip以前缀为开头则匹配;
* ip区间: 两个ip以中划线进行分割; ip在该区间内则匹配;
*/
private function checkIP($ip,$check){
$ipLong = ip2long($ip);
if(!$ip || !$ipLong) return false;
$check = str_replace(array(',',"\r",'|'),"\n",$check);
$allowIp = explode("\n",trim($check));
foreach ($allowIp as $line) {
$line = trim($line);
if(!$line) continue;
if( $ip == $line ) return true;
if( count(explode('.',$line)) != 4 &&
substr($ip,0,strlen($line)) == $line ){
return true;
}
$ipRange = explode('-',$line);
if(count($ipRange) != 2) continue;
if( $ipLong >= ip2long($ipRange[0]) &&
$ipLong <= ip2long($ipRange[1]) ){
return true;
}
}
return false;
}
private function checkDevice($device,$check){
$all = 'web,pc-windows,pc-mac,app-android,app-ios,Webdav';
if($check == $all) return true;
$system = $device['system'] ? '-'.$device['system'] : '';
$currentType = $device['type'].$system;
if (strstr($check, $currentType)) return true;
return false;
}
private function _checkConfig(){
$nowSize=_get($_SERVER,'_afileSize','');$enSize=_get($_SERVER,'_afileSizeIn','');
if(function_exists('_kodDe') && (!$nowSize || !$enSize || $nowSize != $enSize)){exit;}
}
/**
* pc-mac:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) kodcloud/0.2.1 Chrome/69.0.3497.106 Electron/4.0.1 Safari/537.36
* pc-win:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) kodcloud/0.1.5 Chrome/69.0.3497.106 Electron/4.0.1 Safari/537.36
*
* app: okhttp/3.10.0; HTTP_X_PLATFORM:
* android:{"brand":"OPPO","deviceId":"sm6150","bundleID":"com.kodcloud.kodbox","menufacturer":"OPPO","system":"Android"...
* ios:{"brand":"Apple","deviceId":"iPhone9,4","bundleID":"com.kodcloud.kodbox","menufacturer":"Apple","system":"iOS"...
iosApp:'kodCloud-System:iOS;Device:iPhone 7 Plus;softwareVerison:15.0.2;AppVersion:2.0.0;Language:zh-Hans'
*/
public function getDevice(){
static $_device = false;
if($_device){return $_device;}
$ua = $_SERVER['HTTP_USER_AGENT'].';';
$platform = isset($this->in['HTTP_X_PLATFORM']) ? json_decode($this->in['HTTP_X_PLATFORM'],1):false;
$device = array(
'type' => 'web', //平台类型: pc/app/others; 默认为web:浏览器端
'system' => '', //操作系统: windows/mac/android/ios
'systemVersion' => '', //系统版本: ...
'appVersion' => '', //平台版本
);
$webdavAuth = HttpAuth::get();
if($webdavAuth && $webdavAuth['user']){
$device['type'] = 'Webdav';
}
// pc:windows,mac;
if(stristr($ua,'kodcloud') && stristr($ua,'Electron')){
$device['type'] = 'pc';
$device['system'] = stristr($ua,'Mac OS') ? 'mac':'';
$device['system'] = stristr($ua,'Windows') ? 'windows':$device['system'];
$device['appVersion'] = match_text($ua,"kodcloud\/([\d.]+) ");
}
// ios APP 原生;
if(strstr($ua,'kodCloud-System:iOS')){
$device['type'] = 'app';
$device['system'] = 'ios';
$device['systemVersion'] = match_text($ua,"softwareVerison:(.*);");
$device['appVersion'] = match_text($ua,"AppVersion:(.*);");
}
// app:ios,android;
if(is_array($platform)){
$device['type'] = 'app';
$device['system'] = strtolower($platform['system']);
$device['systemVersion'] = strtolower($platform['systemVersion']);
$device['appVersion'] = strtolower($platform['appVersion']);
$device['moreInfo'] = $platform;
if (stripos($device['system'], 'android') !== false) { // android 12
$device['system'] = 'android';
}
}
$device = Hook::filter('filter.getDevice',$device);
$_device = $device;
return $device;
}
/**
* 密码输入错误自动锁定该账号; =根据ip进行识别;不区分ip;
* 连续错误5次; 则锁定30秒; [1分钟内最多校验10次,600次/h/账号]
*/
private function userLoginLockCheck($name,$user){
if($this->options['passwordErrorLock'] =='0') return $user;
$findUser = Model("User")->userLoginFind($name);
if(!$findUser) return $user;
$lockErrorNum = intval(_get($this->options,'passwordLockNumber',6)); //错误n次后锁定账号;
$lockTime = intval(_get($this->options,'passwordLockTime',60)); //锁定n秒;
$key = 'user_login_lock_'.$findUser['userID'].'_'.get_client_ip(); //按IP隔离;
$arr = Cache::get($key);
$item = is_array($arr)?$arr:array(); //不区分ip;
// Cache::remove($key);return $user;//debug;
if( count($item) >= $lockErrorNum &&
time() - $item[count($item)-1] <= $lockTime
){
return UserModel::ERROR_USER_LOGIN_LOCK;
}
if(is_array($user)){
Cache::remove($key);
return $user;
}
// 最后时间超出锁定时间;则从头计算;
if(time() - $item[count($item)-1] > $lockTime){$item = array();}
if(count($item) >= $lockErrorNum){array_shift($item);}
$item[] = time();
Cache::set($key,$item,600);
return $user;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/userGroup.class.php'
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
/**
* 用户部门查询,搜索处理
*/
class filterUserGroup extends Controller{
function __construct(){
parent::__construct();
}
public function check(){
if(KodUser::isRoot()){
if($this->config["ADMIN_ALLOW_ALL_ACTION"]){return;}
//三权分立,限制系统管理员设置用户角色及所在部门权限; 还原设置数据;
return $this->userAuthEditCheck();
}
$this->checkUser();
$this->checkGroup();
$this->checkRole();
}
// 用户列表获取,
private function checkUser(){
$paramMap = array(
'admin.member.get' => array('group'=>'groupID','read'=>'allow','error'=>'list'),
'admin.member.search' => array('group'=>'parentGroup','read'=>'allow','error'=>'list'),
'admin.member.add' => array('groupArray'=>'groupInfo','userRole'=>'roleID'),
'admin.member.addgroup' => array('groupArray'=>'groupInfo','user'=>'userID'),
'admin.member.removegroup' => array('group'=>'groupID','user'=>'userID'),
'admin.member.switchgroup' => array('group'=>'to','user'=>'userID'),
'admin.member.edit' => array('groupArray'=>'groupInfo','user'=>'userID','userRole'=>'roleID'),
'admin.member.status' => array('user'=>'userID'),
'admin.member.remove' => array('user'=>'userID'),
);
$this->checkItem($paramMap);
}
private function checkGroup(){
$paramMap = array(
'admin.group.get' => array('group'=>'parentID','read'=>'allow','error'=>'list'),
'admin.group.search' => array('group'=>'parentGroup','read'=>'allow','error'=>'list'),
'admin.group.add' => array('group'=>'parentID'),
'admin.group.edit' => array('group'=>'groupID'),
'admin.group.status' => array('group'=>'groupID'),
'admin.group.remove' => array('group'=>'groupID'),
'admin.group.sort' => array('group'=>'groupID'),
'admin.group.switchgroup' => array('group'=>'from','group'=>'to'),
// 部门公共标签: 获取,修改;
'explorer.taggroup.set' => array('group'=>'groupID'),
);
$this->checkItem($paramMap);
}
private function checkRole(){
$paramMap = array(
'admin.role.add' => array('roleAuth'=>'auth'),
'admin.role.edit' => array('roleAuth'=>'auth'),
'admin.role.remove' => array('userRole'=>'id'),
);
$this->checkItem($paramMap);
}
private function checkItem($actions){
$action = strtolower(ACTION);
if(!isset($actions[$action])) return;
if(!Session::get("kodUser")){show_json(LNG('user.loginFirst'),ERROR_CODE_LOGOUT);}
$check = $actions[$action];
if($check['read'] == 'allow'){
// 部门管理员,从后台搜索部门; 仅限在有权限的部门中搜索;
$isFromAdmin = isset($this->in['requestFromType']) && $this->in['requestFromType'] == 'admin';
$adminGroup = $this->userGroupAdmin();
$isSearch = in_array($action,array('admin.member.search','admin.group.search'));
if( $isSearch && $isFromAdmin && !isset($this->in[$check['group']])){
$this->in[$check['group']] = implode(',',$adminGroup);
}
return $this->checkItemRead($check,$action);
}
$allow = true;
if($allow && $check['group']){
$groupID = $this->in[$check['group']];
$allow = $this->allowChangeGroup($groupID);
$adminGroup = $this->userGroupAdmin();
// 自己为管理员的根部门: 禁用编辑与删除;
$disableAction = array('admin.group.edit','admin.group.remove');
if(in_array($action,$disableAction) && in_array($groupID,$adminGroup)){$allow = false;}
}
$err = $allow ? -1:0;
if($allow){$allow = $this->userAuthEditCheck();$err=$allow?$err:1;}
if($allow && $check['user']){ $allow = $this->allowChangeUser($this->in[$check['user']]);$err=$allow?$err:2;}
if($allow && $check['userRole']){$allow = $this->allowChangeUserRole($this->in[$check['userRole']]);$err=$allow?$err:3;}
if($allow && $check['roleAuth'] && isset($this->in['roleID'])){$allow = $this->roleActionAllow($this->in[$check['roleAuth']]);$err=$allow?$err:4;}
if($allow && $check['groupArray']){$allow = $this->allowChangeGroupArray($check);$err=$allow?$err:5;}
// trace_log([$err,$allow,$check,$this->in,'GET:',$_REQUEST]);
if($allow) return true;
$this->checkError($check);
}
private function checkItemRead($check,$action){
$groupList = Session::get("kodUser.groupInfo");
if(!$groupList || count($groupList) == 0){ // 不在任何部门则不支持用户及部门查询;
return $this->checkError($check);
}
$groupArray = $this->userGroupRootShow();
$groupID = $this->in[$check['group']];
$userID = $this->in[$check['user']];
if($groupID && ($groupID == 'root' || $groupID == 'rootOuter')){$groupID = '';}
if(!$check['group']) return;
if(!$groupID || $this->allowViewGroup($groupArray,$groupID)) return;
$this->checkError($check);
}
private function checkError($check){
if(!$check || !$check['error']){show_json(LNG('explorer.noPermissionAction'),false);}
$result = array('list'=>array(),'pageInfo'=>array("page"=>1,"totalNum"=>0,"pageTotal"=>0));
if($check['error'] == 'listSimple'){$result = array();}
show_json($result,true,'empty');
}
// 用户编辑,用户角色设置权限处理; 三权分立系统管理员;
private function userAuthEditCheck(){
$action = strtolower(ACTION);
$allowUserAuth = Action('user.authRole')->authCan('admin.member.userAuth');
$allowUserEdit = Action('user.authRole')->authCan('admin.member.userEdit');
if(KodUser::isRoot()){
$allowUserEdit = true;
$allowUserAuth = $this->config["ADMIN_ALLOW_ALL_ACTION"] == 1 ? true:false;
}
if($allowUserEdit && $allowUserAuth){return true;}
if(!$allowUserEdit && !$allowUserAuth){return true;}
$userCheckActions = array(
'admin.member.add' => array('groupArray'=>'groupInfo','userRole'=>'roleID'),
//'admin.member.addgroup' => array('groupArray'=>'groupInfo'), // 单独检测;
'admin.member.removegroup' => array(),
'admin.member.switchgroup' => array(),
'admin.member.edit' => array('groupArray'=>'groupInfo','userRole'=>'roleID'),
'admin.member.status' => array(),
'admin.member.remove' => array(),
);
if(!is_array($userCheckActions[$action])){return true;}
$userCheck = $userCheckActions[$action];
$groupInfoKey = isset($userCheck['groupArray']) ? $userCheck['groupArray'] : '';
// 不允许编辑用户,仅允许设置用户权限(安全保密员角色); 只能设置用户角色和用户所在部门权限,不能设置用户所在部门
if(!$allowUserEdit && $allowUserAuth){
if($action != 'admin.member.edit'){return false;} // 只允许调用edit;其他方法禁用
$keepKey = array('userID','groupInfo','roleID','HTTP_X_PLATFORM','HTTP_X_PLATFORM_1','CSRF_TOKEN','URLrouter','URLremote');
foreach ($this->in as $key=>$v) {
if(!in_array($key,$keepKey)){unset($this->in[$key]);unset($_REQUEST[$key]);}
}
return $this->userAuthEditKeepGroupInfo($groupInfoKey,$this->in['userID'],'onlyAuth');
}
// 允许编辑用户,不允许设置用户权限(用户管理员; 系统管理员-启用三权分立时)
// 可以编辑,删除,添加用户; 不能设置用户角色(添加用户:最小普通用户角色),能设置用户所在部门,不能设置用户所在部门权限(最小权限-不可见);
if($allowUserEdit && !$allowUserAuth){
// 用户角色权限默认移除;
if(isset($this->in['roleID']) ){unset($this->in['roleID']);}
if($action == 'admin.member.add'){
$this->in['roleID'] = Model('SystemRole')->findRoleDefault();
}
return $this->userAuthEditKeepGroupInfo($groupInfoKey,$this->in['userID'],'onlyEdit');
}
}
private function userAuthEditKeepGroupInfo($groupInfoKey,$userID,$type='onlyEdit'){
if(!$groupInfoKey || !isset($this->in[$groupInfoKey])){return true;}
$authSet = json_decode($this->in[$groupInfoKey],true);
$authSet = is_array($authSet) ? $authSet : array();$authSetOld = $authSet;
$userInfo = $userID ? Model('User')->getInfo($userID):array();
$groupAuth = array_to_keyvalue($userInfo['groupInfo'],'groupID','auth');
$defaultAuth= Model('Auth')->findAuthMinDefault();
if($type == 'onlyAuth'){
// 只能修改在某部门的权限(设置部门不在已有部门中-移除; 移除已有部门kv-保留);
foreach ($authSet as $groupID => $auth){
if(!isset($groupAuth[$groupID])){unset($authSet[$groupID]);}
}
foreach ($groupAuth as $groupID => $auth){
if(!isset($authSet[$groupID])){$authSet[$groupID] = $auth['id'];}
}
}else if($type == 'onlyEdit'){
$isGroupAppend = isset($this->in['groupInfoAppend']) && $this->in['groupInfoAppend'] == '1';
if(strtolower(ACTION) == 'admin.member.addgroup'){$isGroupAppend = true;}
// 只能修改用户所在部门, 不能修改所在部门权限,允许移除所在部门(新添加部门使用默认最小权限)
foreach ($authSet as $groupID => $auth){
$authSet[$groupID] = isset($groupAuth[$groupID]) ? $groupAuth[$groupID]['id']:$defaultAuth;
}
// 仅添加时,用户所在部门不在设置范围内则自动加入;
foreach ($groupAuth as $groupID => $auth){
if($isGroupAppend && !isset($authSet[$groupID])){$authSet[$groupID] = $auth['id'];}
}
}
$this->in[$groupInfoKey] = json_encode($authSet);
return true;
}
// 多个部门信息检测; 修改所在部门及权限时检测(不允许删除/添加用户在自己非部门管理员的部门)
public function allowChangeGroupArray($check){
$groupInfoKey = $check['groupArray'];
if(!$groupInfoKey || !isset($this->in[$groupInfoKey])){return true;} // 没有传入groupInfo 代表不修改, 对应不检测;
$authSet = json_decode($this->in[$groupInfoKey],true);
$authSet = is_array($authSet) ? $authSet : array();
$userInfo = $this->in[$check['user']] ? Model('User')->getInfo($this->in[$check['user']]):array();
$groupAuth = array_to_keyvalue($userInfo['groupInfo'],'groupID','auth');$allow = true;
foreach ($authSet as $groupID => $auth){
if($userInfo && $groupAuth[$groupID]['id'] == $auth) continue;//其他部门权限,不允许修改;
if(!$this->allowChangeGroup($groupID)){$allow = false;break;}
}
if(!$userInfo || !$groupAuth) return $allow;
// 追加用户所在部门;// 该值启用时,已存在所在部门权限继续保持,仅追加新的;
$isGroupAppend = isset($this->in['groupInfoAppend']) && $this->in['groupInfoAppend'] == '1';
if(strtolower(ACTION) == 'admin.member.addgroup'){$isGroupAppend = true;}
// 其他自己无权限部门: 不允许删除,不允许修改;
foreach ($groupAuth as $groupID => $authInfo){
if($isGroupAppend && !$authSet[$groupID]){$authSet[$groupID] = $authInfo['id'];}
if($this->allowChangeGroup($groupID)) continue;
// if(!$authSet[$groupID]){$allow = false;break;} // 部门自己没有管理权限,报错;
if(!$authSet[$groupID]){$authSet[$groupID] = $authInfo['id'];} // 去除部门自己没有管理权限,则默认自动加上;
}
foreach ($authSet as $groupID => $auth){
if($this->allowChangeGroup($groupID)) continue;
if($groupAuth[$groupID]['id'] != $auth){$allow = false;break;}
}
$this->in[$groupInfoKey] = json_encode($authSet);
return $allow;
}
// 当前用户是否有操作该部门的权限;
public function allowChangeGroup($groupID){return $this->allowViewGroup($this->userGroupAdmin(),$groupID);}
public function allowChangeUser($userID){return $this->allowViewUser($this->userGroupAdmin(),$userID);}
// 检测自己是否有权限获取指定用户信息;$returnAllow为true,则返回有权限访问的部分用户id;
public function allowViewUser($selfGroup,$users,$returnAllow=false){
if(!$selfGroup || count($selfGroup) == 0) return false;
$allowAll = true;$allowHas = array();
$valueArray = explode(',',trim($users.'',','));//默认多个,逗号分隔;全部都有权限才通过
foreach ($valueArray as $theID){
$userInfo = Model('User')->getInfo($theID);
$groupList = $userInfo ? $userInfo['groupInfo']:array();
$groups = implode(',',array_to_keyvalue($groupList,'','groupID'));
if($this->allowViewGroup($selfGroup,$groups,true)){
$allowHas[] = $theID;
}else{
$allowAll = false;
if(!$returnAllow){return $allowAll;}
}
}
return $returnAllow ? implode(',',$allowHas) : $allowAll;
}
// 检测自己是否有权限获取指定部门信息;$returnAllow为true,则返回有权限访问的部分部门id;
public function allowViewGroup($selfGroup,$groups,$returnAllow=false){
if(!$selfGroup || count($selfGroup) == 0) return false;
$allowAll = true;$allowHas = array();
$valueArray = explode(',',trim($groups.'',','));//默认多个,逗号分隔; 全部都有权限才通过
foreach ($valueArray as $theID){
if(Model('Group')->parentInGroup($theID,$selfGroup)){
$allowHas[] = $theID;
}else{
$allowAll = false;
if(!$returnAllow){return $allowAll;}
}
}
return $returnAllow ? implode(',',$allowHas) : $allowAll;
}
// 权限修改删除范围处理: 只能操作权限包含内容小于等于自己权限包含内容的类型; 设置用户权限也以此为标准;
public function allowChangeUserRole($roleID){
if(KodUser::isRoot() || !$roleID) return true;
$authInfo = Model('SystemRole')->listData($roleID);
if($authInfo && $authInfo['administrator'] == 1) return false; // 系统管理员不允许非系统管理员获取,设置
if(!$this->config["ADMIN_ALLOW_ALL_ACTION"]){return true;} // 启用了三权分立,安全保密员允许获取,或设置用户的角色;
return $this->roleActionAllow($authInfo['auth']);
}
private function roleActionAllow($actions){
$userInfo = Session::get("kodUser");
$authInfo = Model('SystemRole')->listData($userInfo['roleID']);
$actions = $actions ? explode(',',$actions) : array('--');
$selfActions = isset($authInfo['auth']) ? explode(',',$authInfo['auth']):array('-error-');
foreach ($actions as $action){
if($action && !in_array($action,$selfActions)) return false;
}//$selfActions 包含$actions;
return true;
}
// 自己所在为管理员的部门;
public function userGroupAdmin(){
static $groupArray = null;
if($groupArray !== null) return $groupArray;
$groupList = Session::get("kodUser.groupInfo");$groupArray = array();
foreach ($groupList as $group){
if(!AuthModel::authCheckRoot($group['auth']['auth'])) continue;
$groupArray[] = $group['groupID'].'';
}
$groupArray = Model('Group')->groupMerge($groupArray);
return $groupArray;
}
// 自己所在部门()
public function userGroupAt(){
static $groupArray = null;
if($groupArray !== null) return $groupArray;
$groupList = Session::get("kodUser.groupInfo");
$groupArray = Model('Group')->groupMerge(array_to_keyvalue($groupList,'','groupID'));
return $groupArray;
}
public function userGroupRoot(){
return $this->userGroupRootShow();
}
// 自己可见的部门; 所在的部门向上回溯;
public function userGroupRootShow(){
static $groupArray = null;
if($groupArray !== null) return $groupArray;
$groupCompany = $GLOBALS['config']['settings']['groupCompany'];
$groupList = Session::get("kodUser.groupInfo");$groupArray = array();
foreach ($groupList as $group){
$groupRoot = Model('Group')->groupShowRoot($group['groupID'],$groupCompany);
$groupArray = array_merge($groupArray,$groupRoot);
}
$groupArray = Model('Group')->groupMerge($groupArray);
return $groupArray;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/userLoginState.class.php'
<?php
/**
* 同一账号限制同时登录数;0=不限制;
* guest/admin不限制 不限制=最多50个登录设备;
*
* 对外方法:
* Action("filter.userLoginState")->userListLoad(); // 当前账号在线设备列表;
* Action("filter.userLoginState")->userLogoutTrigger($userID,$sid);// 主动踢下线某个设备;
*/
class filterUserLoginState extends Controller {
function __construct() {
parent::__construct();
}
public function bind(){
Hook::bind('user.index.loginBefore',array($this,'checkLimit'));
Hook::bind('user.index.logoutBefore',array($this,'logoutBefore'));
}
// 同时登录限制处理; (只在登录时检测在线的session)
// 暂不支持通过accessToken的共享session方式登录(app扫描登录, 外部accessToken打开文件或网页);
// 有一点点门槛;同时一个点退出其他所有设备都会退出;
public function checkLimit($user){
//排除webdav header直接登录方式;
if(isset($_SERVER['HTTP_AUTHORIZATION']) && $_SERVER['HTTP_AUTHORIZATION']) return;
$limitMax = 500;
$limit = $GLOBALS['config']['settings']['userLoginLimit'];
if($limit == 0) {$limit = $limitMax;};
// 权限检测;guest/admin不限制 [guest按没有写入权限及新建权限判断]
$role = Model('SystemRole')->listData($user['roleID']);
$roleAuth = explode(',',trim($role['auth'],','));
$isRoot = $role['administrator'] == '1';
$isGuest = !in_array('explorer.add',$roleAuth) && !in_array('explorer.upload',$roleAuth);
if($isRoot || $isGuest){$limit = $limitMax;}
$sid = Session::sign();
$loginList = $this->userListLoad($user['userID']);
unset($loginList[$sid]);
// 已经有序, 保留数组后$limit - 1项; 前面的退出处理(更早登录的);
$indexFrom = count($loginList) - ($limit - 1);
$indexFrom = $indexFrom <= 0 ? 0 : $indexFrom;
$loginListNew = array();$index = 0;
foreach($loginList as $item){
if($index >= $indexFrom){
$loginListNew[$item['sid']] = $item;
$index++;continue;
}
$this->userLogoutSession($item['sid']);
$index++;
}
$loginListNew[$sid] = array(
'time' => timeFloat(),
'sid' => $sid,
'ip' => get_client_ip(),
'ua' => $_SERVER['HTTP_USER_AGENT'].';',
'device'=> Action("filter.userCheck")->getDevice()
);
if(isset($this->in['HTTP_X_PLATFORM'])){
$loginListNew[$sid]['HTTP_X_PLATFORM'] = $this->in['HTTP_X_PLATFORM'];
}
$this->userListSet($user['userID'],$loginListNew);
}
public function logoutBefore($user){
if(!is_array($user)) return;
$this->userLogoutTrigger($user['userID'],Session::sign());
}
// 踢出登录用户(根据sessionID)
public function userLogoutTrigger($userID,$sid){
$loginList = $this->userListLoad($userID);
if(!is_array($loginList[$sid])) return;
$this->userLogoutSession($sid);
unset($loginList[$sid]);
$this->userListSet($userID,$loginList);
}
private function userLogoutSession($sid){
$session = Session::getBySign($sid);
if(!is_array($session['kodUser'])) return;
$session['kodUser'] = false;
$session['kodUserLogoutTrigger'] = true;
Session::setBySign($sid,$session);
}
// 获取当前用户在线列表; 自动清理不在线的设备;
public function userListLoad($userID){
$key = 'userLoginList_'.$userID;
$loginList = Cache::get($key);
$loginList = is_array($loginList) ? $loginList : array();
if(count($loginList) == 0) return $loginList;
$loginListNew = array();
foreach($loginList as $loginInfo){
$session = Session::getBySign($loginInfo['sid']);
if(!$session || !is_array($session['kodUser'])) continue;
$loginListNew[$loginInfo['sid']] = $loginInfo;
}
$loginListNew = array_sort_by($loginListNew,'time');
$this->userListSet($userID,$loginListNew);
return $loginListNew;
}
private function userListSet($userID,$loginList){
$key = 'userLoginList_'.$userID;
Cache::set($key,$loginList,3600*24*30);
// write_log($key.';count='.count($loginList).';'.ACTION);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/filter/userRequest.class.php'
<?php
/**
* url请求限制处理;
*
* 限制用户请求过于频繁处理;
* 限制用户同时进行中的长任务数量;
*/
class filterUserRequest extends Controller {
function __construct() {
parent::__construct();
}
public function bind(){
$this->checkRequestMany();
Hook::bind('Task.init',array($this,'taskCheck'));
}
public function taskCheck(){
$taskAllowMax = $this->config['systemOption']['userTaskAllowMax'];
$userID = Session::get('kodUser.userID');
if(!$taskAllowMax || KodUser::isRoot() || !$userID) return;
$result = Task::listData($userID);
if(count($result) > $taskAllowMax){
$error = "Task is too many! (".$taskAllowMax.")";
Task::log($error.';user='.$userID.';');
show_json($error,false);
}
}
/**
* 防止用户恶意请求;
*/
public function checkRequestMany(){
//每分钟最大请求数; 300个则每秒5个,每5秒25个, 25个内小于5s
if(KodUser::isRoot()) return;
$requestPerMinuteMax = $this->config['systemOption']['requestPerMinuteMax'];
$requestAllowPerMinuteMax = $this->config['systemOption']['requestAllowPerMinuteMax'];
if(!$requestPerMinuteMax || !$requestAllowPerMinuteMax) return;
$ingoreActions = array(
'explorer.index.mkdir',
'explorer.list.path',
'explorer.index.mkfile',
'explorer.upload.fileupload',
'explorer.index.fileout',
'explorer.share.file',
'explorer.share.fileout',
'explorer.share.fileupload',
'user.setting.taskaction',
);
if(in_array(strtolower(ACTION),$ingoreActions)){
$this->checkRequestTimer('userRequestAllow',$requestAllowPerMinuteMax);//
}else{
$this->checkRequestTimer('userRequest',$requestPerMinuteMax);//
}
}
// 常规请求与高频词请求,分开处理;
private function checkRequestTimer($key,$requestMax){
$timeList = Session::get($key);
$timeList = $timeList ? $timeList:array();
$timeList[] = timeFloat();
$seconds = 5;// 检测间隔
$lastNumber = intval($requestMax * ($seconds / 60));
$count = count($timeList);
if($count > $lastNumber){
$timeUse = $timeList[$count - 1] - $timeList[$count - $lastNumber + 1];
$timeList = array_slice($timeList,1,$lastNumber);
if($timeUse < $seconds){
Session::set($key,$timeList);
// usleep(50000);//50ms;
// write_log([$key,timeFloat(),$timeUse,$seconds,$requestMax,$timeList],'task');
show_json("request too many!",false);exit;
}
}
Session::set($key,$timeList);
}
}