Files
aaPanel/class/userlogin.py
T
2019-11-22 16:55:21 +08:00

257 lines
12 KiB
Python

#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 宝塔软件(http:#bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: 黄文良 <287962566@qq.com>
# +-------------------------------------------------------------------
import public,os,sys,db,time,json
from BTPanel import session,cache,json_header
from flask import request,redirect,g
class userlogin:
def request_post(self,post):
if not hasattr(post, 'username') or not hasattr(post, 'password'):
return public.returnJson(False,'LOGIN_USER_EMPTY'),json_header
self.error_num(False)
if self.limit_address('?') < 1: return public.returnJson(False,'LOGIN_ERR_LIMIT'),json_header
post.username = post.username.strip();
password = public.md5(post.password.strip());
sql = db.Sql();
userInfo = sql.table('users').where("id=?",(1,)).field('id,username,password').find()
m_code = cache.get('codeStr')
if 'code' in session:
if session['code']:
if not hasattr(post, 'code'): return public.returnMsg(False,'Verification code can not be empty!')
if not public.checkCode(post.code):
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_CODE',('****','****',public.GetClientIp()));
return public.returnJson(False,'CODE_ERR'),json_header
try:
s_pass = public.md5(public.md5(userInfo['password'] + '_bt.cn'))
if userInfo['username'] != post.username or s_pass != password:
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()));
num = self.limit_address('+');
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
_key_file = "/www/server/panel/data/two_step_auth.txt"
if hasattr(post,'vcode'):
if self.limit_address('?',v="vcode") < 1: return public.returnJson(False,'您多次验证失败,禁止10分钟'),json_header
import pyotp
secret_key = public.readFile(_key_file)
if not secret_key:
return public.returnMsg(False, "Did not find the key, please close Google verification on the command line and trun on again")
t = pyotp.TOTP(secret_key)
result = t.verify(post.vcode)
if not result:
if public.sync_date(): result = t.verify(post.vcode)
if not result:
num = self.limit_address('++',v="vcode")
return public.returnJson(False, 'Invalid Verification code. You have [{}] times left to try!'.format(num)), json_header
now = int(time.time())
public.writeFile("/www/server/panel/data/dont_vcode_ip.txt",json.dumps({"client_ip":public.GetClientIp(),"add_time":now}))
self.limit_address('--',v="vcode")
return self._set_login_session(userInfo)
acc_client_ip = self.check_two_step_auth()
if not os.path.exists(_key_file) or acc_client_ip:
return self._set_login_session(userInfo)
self.limit_address('-')
return "1"
except Exception as ex:
stringEx = str(ex)
if stringEx.find('unsupported') != -1 or stringEx.find('-1') != -1:
os.system("rm -f /tmp/sess_*")
os.system("rm -f /www/wwwlogs/*log")
public.ServiceReload()
return public.returnJson(False,'USER_INODE_ERR'),json_header
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()));
num = self.limit_address('+');
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
def request_tmp(self,get):
try:
if not hasattr(get,'tmp_token'): return public.returnJson(False,'INIT_ARGS_ERR'),json_header
save_path = '/www/server/panel/config/api.json'
data = json.loads(public.ReadFile(save_path))
if not 'tmp_token' in data or not 'tmp_time' in data: return public.returnJson(False,'VERIFICATION_FAILED'),json_header
if (time.time() - data['tmp_time']) > 120: return public.returnJson(False,'EXPIRED_TOKEN'),json_header
if get.tmp_token != data['tmp_token']: return public.returnJson(False,'INIT_TOKEN_ERR'),json_header
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','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()));
self.limit_address('-');
cache.delete('panelNum')
cache.delete('dologin')
sess_input_path = 'data/session_last.pl'
public.writeFile(sess_input_path,str(int(time.time())))
del(data['tmp_token'])
del(data['tmp_time'])
public.writeFile(save_path,json.dumps(data))
self.set_request_token()
self.login_token()
return redirect('/')
except:
return public.returnJson(False,'Login failed,' + public.get_error_info()),json_header
def login_token(self):
import config
config.config().reload_session()
self.clear_session()
def request_get(self,get):
#if os.path.exists('/www/server/panel/install.pl'): raise redirect('/install');
if not 'title' in session: session['title'] = public.getMsg('NAME');
domain = public.readFile('data/domain.conf')
if domain:
if(public.GetHost().lower() != domain.strip().lower()):
errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error2.html')
try:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_TITLE'),public.getMsg('PAGE_ERR_DOMAIN_H1'),public.getMsg('PAGE_ERR_DOMAIN_P1'),public.getMsg('PAGE_ERR_DOMAIN_P2'),public.getMsg('PAGE_ERR_DOMAIN_P3'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP'))
except IndexError:pass
return errorStr
if os.path.exists('data/limitip.conf'):
iplist = public.readFile('data/limitip.conf')
if iplist:
iplist = iplist.strip();
if not public.GetClientIp() in iplist.split(','):
errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error2.html')
try:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_TITLE'),public.getMsg('PAGE_ERR_IP_H1'),public.getMsg('PAGE_ERR_IP_P1',(public.GetClientIp(),)),public.getMsg('PAGE_ERR_IP_P2'),public.getMsg('PAGE_ERR_IP_P3'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP'))
except IndexError:pass
return errorStr
sql = db.Sql()
if 'login' in session:
if session['login'] == True:
return redirect('/')
if not 'code' in session:
session['code'] = False
self.error_num(False)
#生成request_token
def set_request_token(self):
session['request_token_head'] = public.GetRandomString(48)
#防暴破
def error_num(self,s = True):
nKey = 'panelNum'
num = cache.get(nKey)
if not num:
cache.set(nKey,1)
num = 1
if s: cache.inc(nKey,1)
if num > 6: session['code'] = True;
#IP限制
def limit_address(self,type,v=""):
import time
clientIp = public.GetClientIp();
numKey = 'limitIpNum_' + v + clientIp
limit = 6;
outTime = 600;
try:
#初始化
num1 = cache.get(numKey)
if not num1:
cache.set(numKey,1,outTime);
num1 = 1;
#计数
if type == '+':
cache.inc(numKey,1)
self.error_num();
session['code'] = True;
return limit - (num1+1);
#计数验证器
if type == '++':
cache.inc(numKey,1)
self.error_num();
session['code'] = False;
return limit - (num1+1);
#清空
if type == '-':
cache.delete(numKey);
session['code'] = False;
return 1;
#清空验证器
if type == '--':
cache.delete(numKey);
session['code'] = False;
return 1;
return limit - num1;
except:
return limit;
# 登录成功设置session
def _set_login_session(self,userInfo):
try:
session['login'] = True
session['username'] = userInfo['username']
public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()))
self.limit_address('-')
cache.delete('panelNum')
cache.delete('dologin')
sess_input_path = 'data/session_last.pl'
public.writeFile(sess_input_path,str(int(time.time())))
self.set_request_token()
self.login_token()
return public.returnJson(True,'LOGIN_SUCCESS'),json_header
except Exception as ex:
stringEx = str(ex)
if stringEx.find('unsupported') != -1 or stringEx.find('-1') != -1:
os.system("rm -f /tmp/sess_*")
os.system("rm -f /www/wwwlogs/*log")
public.ServiceReload()
return public.returnJson(False,'USER_INODE_ERR'),json_header
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()))
num = self.limit_address('+');
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
# 检查是否需要进行二次验证
def check_two_step_auth(self):
dont_vcode_ip_info = public.readFile("/www/server/panel/data/dont_vcode_ip.txt")
acc_client_ip = False
if dont_vcode_ip_info:
dont_vcode_ip_info = json.loads(dont_vcode_ip_info)
ip = dont_vcode_ip_info["client_ip"] == public.GetClientIp()
now = int(time.time())
v_time = now - int(dont_vcode_ip_info["add_time"])
if ip and v_time < 86400:
acc_client_ip = True
return acc_client_ip
# 清理多余SESSION数据
def clear_session(self):
session_file = '/dev/shm/session.db'
if not os.path.exists(session_file): return False
s_size = os.path.getsize(session_file)
if s_size < 1024 * 512: return False
try:
sid = 'BT_:' + session.sid
import db
sql = db.Sql()
sql._Sql__DB_FILE = session_file
if s_size > 1024 * 1024 * 10:
sql.table('session').where('session_id!=?',(sid,)).delete()
sql.table('session').execute('VACUUM',())
else:
sql.table('session').where('session_id!=? AND expiry<?',(sid,public.format_date())).delete()
sql.close()
return True
except:
return False