mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-31 20:29:39 +02:00
1. Add disk IO information to the homepage 2. Add a clear list button in the upload window 3. Optimize the background task overhead of the panel 4. Optimize the disk information caching mechanism 5. Panel Pro edition is online 6. Optimize panel resource usage 7. Optimize SSL certificate renewal 8. Fix the problem of reporting an error when the security entrance is empty 9. Fix the problem of infinite recursion when copying directories in extreme cases
247 lines
11 KiB
Python
247 lines
11 KiB
Python
# coding: utf-8
|
|
# +-------------------------------------------------------------------
|
|
# | 宝塔Linux面板
|
|
# +-------------------------------------------------------------------
|
|
# | Copyright (c) 2015-2019 宝塔软件(http://bt.cn) All rights reserved.
|
|
# +-------------------------------------------------------------------
|
|
# | Author: hwliang <hwl@bt.cn>
|
|
# +-------------------------------------------------------------------
|
|
import os
|
|
import sys
|
|
if not 'class/' in sys.path:
|
|
sys.path.insert(0,'class/')
|
|
import public
|
|
import db
|
|
import json
|
|
import time
|
|
import binascii
|
|
import base64
|
|
from BTPanel import session,cache,request
|
|
|
|
class ScanLogin(object):
|
|
# 扫码登录面板
|
|
def scan_login(self, get):
|
|
# 用于小程序
|
|
data = public.GetRandomString(48) + ':' + str(time.time())
|
|
public.writeFile(self.app_path+"login.pl", data)
|
|
return public.returnMsg(True, 'SCAN_QRCORE_SUCCESS_LOGGING_IN')
|
|
|
|
# 验证是否扫码成功
|
|
def is_scan_ok(self, get):
|
|
if os.path.exists(self.app_path+"login.pl"):
|
|
key, init_time = public.readFile(
|
|
self.app_path+'login.pl').split(':')
|
|
if time.time() - float(init_time) < 60:
|
|
return public.returnMsg(True, key)
|
|
session_id = public.get_session_id()
|
|
if cache.get(session_id) == 'True':
|
|
return public.returnMsg(True, 'Scan QRCORE successfully')
|
|
return public.returnMsg(False, '')
|
|
|
|
# 返回二维码地址
|
|
def login_qrcode(self, get):
|
|
tid = public.GetRandomString(12)
|
|
qrcode_str = 'https://app.bt.cn/app.html?&panel_url='+public.getPanelAddr()+'&v=' + public.GetRandomString(3)+'?login&tid=' + tid
|
|
data = public.get_session_id() + ':' + str(time.time())
|
|
public.writeFile(self.app_path + "app_login_check.pl", data)
|
|
cache.set(tid,public.get_session_id(),360)
|
|
cache.set(public.get_session_id(),tid,360)
|
|
return public.returnMsg(True, qrcode_str)
|
|
|
|
#生成request_token
|
|
def set_request_token(self):
|
|
session['request_token_head'] = public.GetRandomString(48)
|
|
|
|
# 设置登录状态
|
|
def set_login(self, get):
|
|
session_id = public.get_session_id()
|
|
if cache.get(session_id) == 'True':
|
|
return self.check_app_login(get)
|
|
|
|
if os.path.exists(self.app_path+"login.pl"):
|
|
data = public.readFile(self.app_path+'login.pl')
|
|
public.ExecShell('rm ' + self.app_path+"login.pl")
|
|
secret_key, init_time = data.split(':')
|
|
if time.time() - float(init_time) < 60 and get['secret_key'] == secret_key:
|
|
sql = db.Sql()
|
|
userInfo = sql.table('users').where(
|
|
"id=?", (1,)).field('id,username,password').find()
|
|
session['login'] = True
|
|
session['username'] = userInfo['username']
|
|
cache.delete('panelNum')
|
|
cache.delete('dologin')
|
|
public.WriteLog('TYPE_LOGIN', 'LOGIN_SUCCESS',
|
|
('WeChat scan code login', public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
|
|
login_type = 'data/app_login.pl'
|
|
self.set_request_token()
|
|
import config
|
|
config.config().reload_session()
|
|
public.writeFile(login_type,'True')
|
|
public.login_send_body("Wechat program",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
|
return public.returnMsg(True, 'login successful')
|
|
return public.returnMsg(False, 'Login failed')
|
|
|
|
|
|
#验证APP是否登录成功
|
|
def check_app_login(self,get):
|
|
#判断是否存在绑定
|
|
btapp_info = json.loads(public.readFile('/www/server/panel/config/api.json'))
|
|
if not btapp_info:return public.returnMsg(False,'Unbound')
|
|
if not btapp_info['open']:return public.returnMsg(False,'API not open')
|
|
if not 'apps' in btapp_info:return public.returnMsg(False,'Unbound phone')
|
|
if not btapp_info['apps']:return public.returnMsg(False,'Unbound phone')
|
|
try:
|
|
session_id=public.get_session_id()
|
|
if not os.path.exists(self.app_path+'app_login_check.pl'):return public.returnMsg(False,'Wait for the app to scan the code and log in 1')
|
|
data = public.readFile(self.app_path+'app_login_check.pl')
|
|
public.ExecShell('rm ' + self.app_path+"app_login_check.pl")
|
|
secret_key, init_time = data.split(':')
|
|
if len(session_id)!=64:return public.returnMsg(False,'Wait for the app to scan the code and log in 2')
|
|
if time.time() - float(init_time) < 180 and session_id != secret_key:
|
|
return public.returnMsg(False,'Wait for the app to scan the code and log in')
|
|
cache.delete(session_id)
|
|
userInfo = public.M('users').where("id=?",(1,)).field('id,username').find()
|
|
session['login'] = True
|
|
session['username'] = userInfo['username']
|
|
session['tmp_login'] = True
|
|
public.WriteLog('TYPE_LOGIN','APP scan code login, account: {}, login IP: {}'.format(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
|
|
cache.delete('panelNum')
|
|
cache.delete('dologin')
|
|
sess_input_path = 'data/session_last.pl'
|
|
public.writeFile(sess_input_path,str(int(time.time())))
|
|
login_type = 'data/app_login.pl'
|
|
self.set_request_token()
|
|
import config
|
|
config.config().reload_session()
|
|
public.writeFile(login_type,'True')
|
|
public.login_send_body("aaPanel Mobile",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
|
return public.returnMsg(True,'login successful!')
|
|
except:
|
|
return public.returnMsg(False, 'login fail')
|
|
|
|
class SelfModule():
|
|
'''
|
|
只能在面板执行的模块
|
|
不允许外部访问
|
|
'''
|
|
|
|
def __init__(self):
|
|
self.user_info_file = self.app_path + "user.json"
|
|
if not os.path.exists(self.user_info_file):
|
|
public.ExecShell("echo '{}' > " + self.user_info_file)
|
|
try:
|
|
self.user_info = json.loads(public.readFile(self.user_info_file))
|
|
except: public.ExecShell("echo '{}' > " + self.user_info_file)
|
|
|
|
user_info_file_app = self.app_path_p + "user.json"
|
|
if os.path.exists(user_info_file_app):
|
|
try:
|
|
user_info_app = json.loads(public.readFile(user_info_file_app))
|
|
for userId in user_info_app.keys():
|
|
if userId in self.user_info: continue;
|
|
self.user_info[userId] = user_info_app[userId];
|
|
except:pass
|
|
|
|
def blind_qrcode(self, get):
|
|
'''
|
|
生成绑定二维码
|
|
'''
|
|
panel_addr = public.getPanelAddr()
|
|
token = public.GetRandomString(32)
|
|
data = '%s:%s' % (token, int(time.time()))
|
|
public.writeFile(self.app_path + 'token.pl',data)
|
|
public.writeFile(self.app_path_p + 'token.pl',data)
|
|
qrcode_str = 'https://app.bt.cn/app.html?panel_url=' + \
|
|
panel_addr+'&panel_token=' + token + '?blind'
|
|
return public.returnMsg(True, qrcode_str)
|
|
|
|
def blind_del(self, get):
|
|
# 删除绑定
|
|
del self.user_info[get['uid']]
|
|
public.writeFile(self.app_path+"user.json", json.dumps(self.user_info))
|
|
public.writeFile(self.app_path_p + "user.json", json.dumps(self.user_info))
|
|
return public.returnMsg(True, 'DEL_SUCCESS')
|
|
|
|
def get_user_info(self, get):
|
|
if session['version'] < '5.8.6':
|
|
return public.returnMsg(False, 'PANEL_TOO_LOW')
|
|
|
|
data = {}
|
|
if not get: data = []
|
|
for k in self.user_info.keys():
|
|
v = self.user_info[k]
|
|
if get:
|
|
del(v['token'])
|
|
data[k] = v
|
|
else:
|
|
data.append(v['nickName'])
|
|
if not get:
|
|
data = ','.join(data);
|
|
if not data: data = public.GetMsg("NOT_BIND_WECHAT");
|
|
return public.returnMsg(True, data)
|
|
|
|
def blind_result(self, get):
|
|
return not os.path.exists(self.app_path + "token.pl")
|
|
|
|
class wxapp(SelfModule, ScanLogin):
|
|
|
|
def __init__(self):
|
|
self.app_path = '/www/server/panel/data/'
|
|
self.app_path_p = '/www/server/panel/plugin/app/'
|
|
SelfModule.__init__(self)
|
|
|
|
def _check(self, get):
|
|
token_data = public.readFile(self.app_path + 'token.pl')
|
|
if not token_data: token_data = public.readFile(self.app_path_p + 'token.pl')
|
|
if hasattr(SelfModule, get['fun']):
|
|
return False
|
|
elif get['fun'] in ['set_login', 'is_scan_ok', 'login_qrcode']:
|
|
return True
|
|
elif get['fun'] == 'blind':
|
|
if not token_data:
|
|
return public.returnMsg(False, 'QRCORE_EXPIRE',("1",))
|
|
token_data = token_data.replace('\n', '')
|
|
password, expiration_time = token_data.split(':')
|
|
# return True
|
|
if time.time() - int(expiration_time) > 8*60:
|
|
return public.returnMsg(False, 'QRCORE_EXPIRE',("2",))
|
|
elif get['panel_token'] != password:
|
|
return public.returnMsg(False, 'SK_NOT_INCORRECT')
|
|
return True
|
|
else:
|
|
# 是否在白名单ip sgin 是否正确
|
|
if hasattr(get, 'uid') and hasattr(get, 'sgin') and hasattr(get, 'fun') and get['uid'] in self.user_info.keys():
|
|
encryption_str = self.user_info[get['uid']]['token']+get['fun']+get['uid']
|
|
if sys.version_info[0] == 3:
|
|
if type(encryption_str) == str:
|
|
encryption_str = encryption_str.encode()
|
|
if get['sgin'] == public.md5(binascii.hexlify(base64.b64encode(encryption_str))):
|
|
if public.GetClientIp() in ['47.52.194.186']:
|
|
return True
|
|
return public.returnMsg(False, 'UNAUTHORIZED')
|
|
|
|
# 用户绑定
|
|
def blind(self, get):
|
|
# 用于小程序
|
|
self.user_info[get['uid']] = {
|
|
"avatarUrl": get['avatarUrl'],
|
|
"nickName": get['nickName'],
|
|
"token": get['token']
|
|
}
|
|
public.writeFile(self.app_path+"user.json", json.dumps(self.user_info))
|
|
public.writeFile(self.app_path_p + "user.json", json.dumps(self.user_info))
|
|
public.ExecShell("rm -rf %stoken.pl" % self.app_path)
|
|
public.ExecShell("rm -rf %stoken.pl" % self.app_path_p)
|
|
return public.returnMsg(True, 'BIND_SUCCESS')
|
|
|
|
|
|
def get_safe_log(self):
|
|
get = {
|
|
'page': 1,
|
|
'count': 10
|
|
}
|
|
print(get['page'] - 1) * get['count'], get['count']
|
|
data = public.M('logs').limit('%s, %s' % (
|
|
(get['page'] - 1) * get['count'], get['count'])).select()
|
|
return data
|