2024-08-31 01:03:37 +08:00

385 lines
14 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
class adminLog extends Controller{
public $actionList = array();
function __construct() {
parent::__construct();
$this->model = Model('SystemLog');
}
// 操作类型列表
private function typeListAll(){
$typeList = $this->model->typeListAll();
if (_get($GLOBALS, 'config.settings.fileViewLog') != 1) {
unset($typeList['file.view']);
}
return $typeList;
}
/**
* 操作类型列表
* this.actions()
* @return void
*/
public function typeList(){
$typeList = $this->typeListAll();
$list = array(
'all' => array('id' => 'all', 'text' => LNG('common.all')),
'file' => array('id' => 'file', 'text' => LNG('admin.log.typeFile')),
'user' => array('id' => 'user', 'text' => LNG('admin.log.typeUser')),
'admin' => array('id' => 'admin','text' => LNG('admin.manage')),
);
foreach($typeList as $type => $name) {
$action = explode('.', $type);
$mod = $action[0];
if(!isset($list[$mod])) continue;
if(!is_array($list[$mod]['children'])){$list[$mod]['children'] = array();}
$list[$mod]['children'][] = array('id' => $type,'text' => $name);
}
$fileList = array(
array('id' => 'explorer.index.zipDownload', 'text' => LNG('admin.log.downFolder')),
array('id' => 'explorer.index.fileOut', 'text' => LNG('admin.log.downFile')),
array('id' => 'explorer.index.fileDownload', 'text' => LNG('admin.log.downFile')),
array('id' => 'explorer.fav.add', 'text' => LNG('explorer.addFav')),
array('id' => 'explorer.fav.del', 'text' => LNG('explorer.delFav')),
);
if(!is_array($list['file']['children'])){$list['file']['children'] = array();}
$list['file']['children'] = array_merge($list['file']['children'], $fileList);
$list = $this->typeListMerge($list);
show_json($list);
}
// 合并操作日志类型;
private function typeListMerge($list){
$mergeList = array(
'file' => array(
// 'file.edit,file.rename' => LNG('admin.log.editFile'),
// 'file.mkdir,file.mkfile' => '新建文件(夹)',
'file.copy,file.move,file.moveOut' => LNG('log.file.move'),
'explorer.fav.add,explorer.fav.del' => LNG('log.file.fav'),
'explorer.index.fileOut,explorer.index.fileDownload' => LNG('admin.log.downFile'),
'file.shareLinkAdd,file.shareLinkRemove' => LNG('log.file.shareLink'),
'file.shareToAdd,file.shareToRemove' => LNG('log.file.shareTo'),
),
'user' => array(
'user.setting.setHeadImage,user.setting.setUserInfo' => LNG('log.user.edit'),
),
'admin' => array(
'admin.group.add,admin.group.edit,admin.group.remove,admin.group.status,admin.group.sort,admin.group.switchGroup' => LNG('log.group.edit'),
'admin.member.add,admin.member.edit,admin.member.remove,admin.member.addGroup,admin.member.removeGroup,admin.member.switchGroup,admin.member.status' => LNG('log.member.edit'),
'admin.role.add,admin.role.edit,admin.role.remove' => LNG('log.role.edit'),
'admin.auth.add,admin.auth.edit,admin.auth.remove' => LNG('log.auth.edit'),
'admin.storage.add,admin.storage.edit,admin.storage.remove' => LNG('admin.menu.storageDriver'),
),
);
foreach($list as $listKey => $item) {
if(!$item['children'] || !$mergeList[$item['id']]) continue;
$actionMake = array();
foreach ($mergeList[$item['id']] as $actions => $text) {
$actionArr = explode(',',$actions);
$actionMake[$actions] = false;//isMerged 是否合并;
foreach ($actionArr as $action) {
$actionMake[$action] = array('data'=>array('id'=> $actions,'text'=> $text),'actions'=>$actions);
}
}
$children = array();
foreach ($item['children'] as $childItem) {
$action = $childItem['id'];
if( isset($actionMake[$action]) ){
$item = $actionMake[$action];
if( !$actionMake[$item['actions']] ){
$children[] = $item['data'];
$actionMake[$item['actions']] = true;
}
}else{
$children[] = $childItem;
}
}
// pr($item,$children,$actionMake);exit;
$list[$listKey]['children'] = $children;
}
$list = array_values($list);
return $list;
}
/**
* 后台管理-日志列表
* @return void
*/
public function get(){
$data = Input::getArray(array(
'timeFrom' => array('check' => 'require'),
'timeTo' => array('check' => 'require'),
'userID' => array('default' => ''),
'type' => array('default' => ''),
'ip' => array('default' => null),
));
// 部门管理员, 只能查询自己为部门管理员部门下的成员操作日志;
if(!KodUser::isRoot()){
$filter = Action("filter.UserGroup");
if($data['userID'] && !$filter->allowChangeUser($data['userID'])){
show_json(LNG('explorer.noPermissionAction'),false);
}
$groupAdmin = $filter->userGroupAdmin();
if(!$data['userID'] && !in_array('1',$groupAdmin)){
$groupAll = Model('Group')->groupChildrenAll($groupAdmin);
$userAll = Model('User')->groupUserAll($groupAll);
if(!$userAll){
show_json(array());
}
$data['userID'] = array('in',$userAll);
}
}
$res = $this->model->get($data);
if(empty($res)) show_json(array());
show_json($res['list'], true, $res['pageInfo']);
}
/**
* 记录日志
* @param boolean $data
* @return void
*/
public function add($data=false){
if (isset($this->in['disableLog']) && $this->in['disableLog'] == '1') return;
$typeList = $this->typeListAll();
if(!isset($typeList[ACTION])) return;
if($GLOBALS['loginLogSaved'] ==1) return;
$actionList = array(
'user.index.logout',
'user.index.loginSubmit',
);
// 操作日志
if(!in_array(ACTION, $actionList)){
// 文件类的操作此处只收集这3个
if(MOD == 'explorer') {
$act = ST . '.' . ACT;
$func = array('fav.add', 'fav.del', 'index.fileOut', 'index.fileDownload', 'index.zipDownload');
if(!in_array($act, $func)) return;
if (in_array(ACT, array('fileOut', 'fileDownload'))) { // 多线程下载,或某些浏览器会请求多次
if (!$this->checkHttpRange()) return;
} else if (ACT == 'zipDownload') {
if (isset($this->in['zipClient']) && $this->in['zipClient'] == '1') {
$data = false; // 前端压缩下载会返回列表,故下方以$this->in赋值
}
}
}
if(!is_array($data)) $data = $this->filterIn();
}
// 第三方绑定
if(ACTION == 'user.index.loginSubmit'){
if (!is_array($data)) return;
return $this->loginLog();
}
return $this->model->addLog(ACTION, $data);
}
/**
* 过滤in中多余参数
* @return void
*/
private function filterIn(){
$in = $this->in;
unset($in['URLrouter'],$in['URLremote'],$in['HTTP_DEBUG_URL'],$in['CSRF_TOKEN'],
$in['viewToken'],$in['accessToken'],$in[str_replace(".", "/", ACTION)]);
return $in;
}
// 文件预览下载,是否为从头开始下载(用于下载计数,或统计日志); 允许的情况:没有range; 有range且start=0且end大于5
public function checkHttpRange(){
if(strtoupper($_SERVER['REQUEST_METHOD']) == 'HEAD'){return false;}
if(!isset($_SERVER['HTTP_RANGE'])){return true;}
$start = 0;$end = 100;
$find = preg_match('/bytes=\s*(\d+)-(\d*)/i',$_SERVER['HTTP_RANGE'],$matches);
if($find && is_array($matches)){$start = intval($matches[1]);}
if(!empty($matches[2])){$end = intval($matches[2]);}
return ($start == 0 && $end >= 5) ? true : false;
}
/**
* 登录日志
* @param string $action
* @param [type] $ip
* @return void
*/
public function loginLog(){
if($GLOBALS['loginLogSaved'] == 1 || !Session::get('kodUser')) return;
$GLOBALS['loginLogSaved'] = 1;
$data = array(
'is_wap' => is_wap(),
'ua' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''
);
if(isset($this->in['HTTP_X_PLATFORM'])) {
$data['is_wap'] = true;
$data['HTTP_X_PLATFORM'] = $this->in['HTTP_X_PLATFORM'];
}
return $this->model->addLog('user.index.loginSubmit', $data);
}
/**
* 个人中心-用户文档日志
* @return void
*/
public function userLog(){
$userID = Input::get('userID', 'int');
// 获取文件操作类型
$typeList = $this->typeListAll();
$types = array();
foreach($typeList as $key => $value) {
if(strpos($key, 'file.') === 0) $types[] = $key;
}
$add = array(
'explorer.index.fileOut',
'explorer.index.fileDownload',
'explorer.index.zipDownload',
'explorer.fav.add',
'explorer.fav.del'
);
$types = array_merge($types, $add);
$data = array(
'userID' => $userID,
'type' => implode(',', $types)
);
$res = $this->model->get($data);
foreach($res['list'] as $i => &$item) {
$value = array(
'type' => $item['type'],
'createTime' => $item['createTime'],
'title' => $item['title'],
'address' => $item['address']
);
$item['desc'] = is_array($item['desc']) ? $item['desc']:array();
$item = array_merge($value, $item['desc']);
};unset($item);
show_json($res);
}
/**
* 个人中心-用户登录日志
* @return void
*/
public function userLogLogin(){
$data = Input::getArray(array(
'type' => array('check' => 'require'),
'userID' => array('check' => 'require'),
));
$res = $this->model->get($data);
if(empty($res)) show_json(array());
show_json($res['list'], true, $res['pageInfo']);
}
/**
* hook绑定
* @return void
*/
public function hookBind(){
// 账号、存储管理删除及编辑等操作只有id没有名称故提前获取
if (MOD == 'admin' && in_array(ACT, array('edit','status','remove'))) {
if (!isset($this->in['name'])) {
switch (ST) {
case 'group':
$info = Model('Group')->getInfoSimple($this->in['groupID']);
break;
case 'member':
$info = Model('User')->getInfoSimple($this->in['userID']);
break;
case 'auth':
case 'role':
$table = ST == 'auth' ? 'Auth' : 'SystemRole';
$info = Model($table)->listData($this->in['id']);
break;
}
if (!empty($info['name'])) $this->in['name'] = $info['name'];
}
}
// 退出时在请求出记录,其他在出执行结果后记录
if (ACTION == 'user.index.logout') {
$user = Session::get('kodUser');
if (!$user) return;
$data = array(
'code' => true,
'data' => array(
'userID' => $user['userID'],
'name' => $user['name'],
'nickName' => $user['nickName'],
)
);
return $this->autoLog($data);
}
// 存储管理部分操作参数只有id
if (MOD.'.'.ST == 'admin.storage' && in_array(ACT,array('add','edit','remove'))) {
if (ACT == 'remove' && isset($this->in['progress'])) return;
if (empty($this->in['name'])) {
$info = Model('Storage')->listData($this->in['id']);
$this->in['name'] = $info['name'];
$this->in['driver'] = $info['driver']; // 谨慎追加参数,避免影响主方法调用
}
}
Hook::bind('show_json', array($this, 'autoLog'));
Hook::bind('explorer.fileDownload', array($this, 'autoLog'));
// 开启了文件预览日志
// explorer.index.fileout
// explorer.editor.fileget
// explorer.share.fileout
// explorer.share.file // 主要用于外链(如用户头像),排除
// explorer.history.fileout
if (_get($GLOBALS, 'config.settings.fileViewLog') == 1) {
Hook::bind('plugin.fileView', array($this, 'fileViewLog'));
Hook::bind('explorer.fileOut', array($this, 'fileViewLog'));
Hook::bind('explorer.fileGet', array($this, 'fileViewLog'));
}
}
// 通用日志
public function autoLog($data){
if (isset($data['code']) && !$data['code']) return false;
if (!isset($data['data']) || !is_array($data)) {
$data = array('data' => $data);
}
// $info = isset($data['info']) ? $data['info'] : null;
$this->add($data['data']);
}
// 文件预览日志
public function fileViewLog($path){
if (MOD == 'plugin' && ACT != 'index') return;
$in = $this->in;
if (strtolower(ACT) == 'fileout') {
// if (isset($in['download']) && $in['download'] == 1) return;
if(isset($in['type']) && $in['type'] == 'image'){
if (isset($in['width']) && $in['width'] == '250') return;
}
// 该参数由插件打开时filePathLink调用fileOut追加排除
if (isset($in['et'])) return;
}
if (isset($in['download']) && $in['download'] == 1) return; // 分享下载fileDownload=>fileOut
if (!$this->checkHttpRange()) return;
// 获取文件信息,写入日志
$parse = KodIO::parse($path);
if ($parse['type'] != KodIO::KOD_SOURCE || !$parse['id']) {
$parse = KodIO::parse($this->in['path']);
if ($parse['type'] != KodIO::KOD_SOURCE || !$parse['id']) return;
}
$sourceID = $parse['id'];
$sourceInfo = Model("Source")->sourceInfo($sourceID);
if(!$sourceInfo || $sourceInfo['targetType'] == SourceModel::TYPE_SYSTEM) return;
// 参考sourceEvent.addSystemLog
$data = array(
"sourceID" => $sourceID,
"sourceParent" => $sourceInfo['parentID'],
"sourceTarget" => $sourceID,
'pathName' => $sourceInfo['name'],
'pathDisplay' => !empty($sourceInfo['pathDisplay']) ? $sourceInfo['pathDisplay'] : '',
"userID" => USER_ID,
"type" => 'view',
"desc" => $this->filterIn(),
);
Model('SystemLog')->addLog('file.view', $data);
}
}