pull/628/head
Mr Chen 6 months ago
parent ebf474aefb
commit 11f42d5d2c
  1. 70
      web/admin/dashboard/__init__.py
  2. 14
      web/admin/firewall/__init__.py
  3. 4
      web/admin/model/__init__.py
  4. 44
      web/admin/model/temp_login.py
  5. 30
      web/admin/model/user.py
  6. 3
      web/admin/setting/__init__.py
  7. 6
      web/admin/setting/main.py
  8. 54
      web/admin/setting/secondary_verifiy.py
  9. 62
      web/admin/setting/temp_login.py
  10. 60
      web/admin/setting/timezone.py
  11. 1
      web/admin/system/__init__.py
  12. 37
      web/core/mw.py
  13. 7
      web/static/app/config.js
  14. 19
      web/utils/firewall.py
  15. 8
      web/utils/mwplugin.py

@ -13,6 +13,7 @@ import time
from flask import Blueprint, render_template from flask import Blueprint, render_template
from flask import make_response from flask import make_response
from flask import redirect
from flask import Response from flask import Response
from flask import request,g from flask import request,g
@ -20,6 +21,8 @@ from admin.common import isLogined
from admin.user_login_check import panel_login_required from admin.user_login_check import panel_login_required
from admin import cache,session from admin import cache,session
from admin import model from admin import model
from admin.model import db,TempLogin,Users
import core.mw as mw import core.mw as mw
@ -77,9 +80,76 @@ def admin_safe_path(path):
# 定义登录入口相关方法 # 定义登录入口相关方法
# --------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------
def getErrorNum(key, limit=None):
key = mw.md5(key)
num = cache.get(key)
if not num:
num = 0
if not limit:
return num
if limit > num:
return True
return False
def setErrorNum(key, empty=False, expire=3600):
key = mw.md5(key)
num = cache.get(key)
if not num:
num = 0
else:
if empty:
cache.delete(key)
return True
cache.set(key, num + 1, expire)
return True
def login_temp_user(token):
if len(token) != 32:
return '错误的参数!'
skey = mw.getClientIp() + '_temp_login'
if not getErrorNum(skey, 10):
return '连续10次验证失败,禁止1小时'
stime = int(time.time())
tmp_data = model.getTempLoginByToken(token)
if not tmp_data:
setErrorNum(skey)
return '验证失败!'
if stime > int(tmp_data['expire']):
setErrorNum(skey)
return "过期"
user_data = model.getUserById(1)
login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT'))
mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr))
TempLogin.query.filter(TempLogin.id==tmp_data['id']).update({"login_time": stime, 'state': 1, 'login_addr': login_addr})
db.session.commit()
session['login'] = True
session['username'] = user_data['name']
session['tmp_login'] = True
session['tmp_login_id'] = str(tmp_data['id'])
session['tmp_login_expire'] = int(tmp_data['expire'])
session['uid'] = user_data['id']
return redirect('/')
# 登录页: 当设置了安全路径,本页失效。 # 登录页: 当设置了安全路径,本页失效。
@blueprint.route('/login') @blueprint.route('/login')
def login(): def login():
# 临时登录功能
token = request.args.get('tmp_token', '').strip()
if token != '':
return login_temp_user(token)
# 注销登录
signout = request.args.get('signout', '') signout = request.args.get('signout', '')
if signout == 'True': if signout == 'True':
session.clear() session.clear()

@ -50,7 +50,7 @@ def get_list():
data = {} data = {}
data['data'] = rows data['data'] = rows
data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p}) data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p,'row':limit})
return data return data
# 获取站点日志目录 # 获取站点日志目录
@ -60,7 +60,7 @@ def get_www_path():
path = mw.getLogsDir() path = mw.getLogsDir()
return {'path': path} return {'path': path}
# 获取站点日志目录 # 获取ssh信息
@blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST']) @blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST'])
@panel_login_required @panel_login_required
def get_ssh_info(): def get_ssh_info():
@ -68,6 +68,16 @@ def get_ssh_info():
return mf.getSshInfo() return mf.getSshInfo()
# 切换ping开关
@blueprint.route('/set_ping', endpoint='set_ping', methods=['POST'])
@panel_login_required
def set_ping():
mf = MwFirewall.instance()
return mf.setPing()

@ -21,5 +21,7 @@ from .task import addTask
from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun
from .task import setTaskStatus,setTaskData from .task import setTaskStatus,setTaskData
from .user import getUserByName,isLoginCheck from .user import getUserByName,getUserById,isLoginCheck
from .temp_login import getTempLoginByToken,clearTempLogin

@ -0,0 +1,44 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
import time
from admin.model import db, TempLogin
import core.mw as mw
def getTempLoginByToken(token,
) -> None:
'''
获取用户信息通过用户名
'''
item = TempLogin.query.filter(TempLogin.token==token).first()
if item is None:
return None
row = {}
row['id'] = item.id
row['token'] = item.token
row['salt'] = item.salt
row['state'] = item.state
row['login_time'] = item.login_time
row['login_addr'] = item.login_addr
row['logout_time'] = item.logout_time
row['expire'] = item.expire
row['add_time'] = item.add_time
return row
def clearTempLogin()->bool:
'''
清空过期数据
'''
now_time = int(time.time())
TempLogin.query.filter(TempLogin.expire<now_time).delete()
db.session.commit()
return True

@ -40,3 +40,33 @@ def isLoginCheck(username, password) -> bool:
if info['password'] == mw.md5(password): if info['password'] == mw.md5(password):
return True return True
return False return False
def getUserById(id,
) -> None:
'''
获取用户信息通过用户名
'''
item = Users.query.filter(Users.id==id).first()
if item is None:
return None
row = {}
row['id'] = item.id
row['name'] = item.name
row['password'] = item.password
row['login_ip'] = item.login_ip
row['login_time'] = item.login_time
row['phone'] = item.phone
row['email'] = item.email
row['add_time'] = item.add_time
row['update_time'] = item.update_time
return row
def isLoginCheck(username, password) -> bool:
info = getUserByName(data['username'])
if info is None:
return False
if info['password'] == mw.md5(password):
return True
return False

@ -10,6 +10,9 @@
from .main import * from .main import *
from .temp_login import * from .temp_login import *
from .timezone import *
from .secondary_verifiy import *

@ -142,7 +142,7 @@ def set_basic_auth():
return mw.returnData(True, '设置成功!') return mw.returnData(True, '设置成功!')
# 默认站点目录 # 设置站点状态
@blueprint.route('/set_status_code', endpoint='set_status_code', methods=['POST']) @blueprint.route('/set_status_code', endpoint='set_status_code', methods=['POST'])
@panel_login_required @panel_login_required
def set_status_code(): def set_status_code():
@ -159,9 +159,7 @@ def set_status_code():
model.setOption('unauthorized_status', str(status_code)) model.setOption('unauthorized_status', str(status_code))
mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text'])) mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text']))
return mw.returnData(True, '设置成功!') return mw.returnData(True, '设置成功!')

@ -0,0 +1,54 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
import re
import json
import os
import time
from flask import Blueprint, render_template
from flask import request
from admin import model
from admin import session
from admin.model import db,TempLogin
from admin.user_login_check import panel_login_required
import core.mw as mw
import utils.config as utils_config
from .main import blueprint
@blueprint.route('/get_auth_secret', endpoint='get_auth_secret', methods=['POST'])
@panel_login_required
def get_auth_secret():
reset = request.form.get('reset', '')
__file = mw.getCommonFile()
import pyotp
auth = __file['auth_secret']
tag = 'mdserver-web'
if os.path.exists(auth) and reset != '1':
content = mw.readFile(auth)
sec = mw.deDoubleCrypt(tag,content)
else:
sec = pyotp.random_base32()
crypt_data = mw.enDoubleCrypt(tag, sec)
mw.writeFile(auth, crypt_data)
ip = mw.getHostAddr()
url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag)
rdata = {}
rdata['secret'] = sec
rdata['url'] = url
return mw.returnData(True, '设置成功!', rdata)

@ -17,6 +17,7 @@ from flask import Blueprint, render_template
from flask import request from flask import request
from admin import model from admin import model
from admin import session
from admin.model import db,TempLogin from admin.model import db,TempLogin
from admin.user_login_check import panel_login_required from admin.user_login_check import panel_login_required
@ -26,14 +27,40 @@ import utils.config as utils_config
from .main import blueprint from .main import blueprint
@blueprint.route('/get_temp_login', endpoint='get_temp_login', methods=['POST'])
@panel_login_required
def get_temp_login():
limit = request.form.get('limit', '5').strip()
p = request.form.get('page', '1').strip()
tojs = request.form.get('tojs', '').strip()
count = TempLogin.query.filter_by().count()
pagination = TempLogin.query.filter_by().order_by(TempLogin.expire.desc()).paginate(page=int(p), per_page=int(limit))
rows = []
for item in pagination.items:
t = {}
t['id'] = item.id
t['salt'] = item.salt
t['state'] = item.state
t['expire'] = item.expire
t['login_time'] = item.login_time
t['login_addr'] = item.login_addr
t['add_time'] = item.add_time
rows.append(t)
data = {}
data['data'] = rows
data['page'] = mw.getPage({'count':count,'tojs':'setTempAccessReq','p':p,'row':limit})
return data
@blueprint.route('/set_temp_login', endpoint='set_temp_login', methods=['POST']) @blueprint.route('/set_temp_login', endpoint='set_temp_login', methods=['POST'])
@panel_login_required @panel_login_required
def set_temp_login(): def set_temp_login():
# if 'tmp_login_expire' in session: # if 'tmp_login_expire' in session:
# return mw.returnData(False, '没有权限') # return mw.returnData(False, '没有权限')
start_time = int(time.time()) start_time = int(time.time())
# mw.M('temp_login').where( model.clearTempLogin()
# 'state=? and expire>?', (0, start_time)).delete()
token = mw.getRandomString(48) token = mw.getRandomString(48)
@ -52,14 +79,33 @@ def set_temp_login():
r = db.session.add(add_tl) r = db.session.add(add_tl)
db.session.commit() db.session.commit()
if r is None:
mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(mw.formatDate(times=expire)))
return {'status': True, 'msg': "临时连接已生成", 'token': make_token, 'expire': expire}
return mw.returnData(False, '连接生成失败')
# if not mw.M('temp_login').count(): @blueprint.route('/remove_temp_login', endpoint='remove_temp_login', methods=['POST'])
# pdata['id'] = 101 @panel_login_required
def remove_temp_login():
tl_id = request.form.get('id', '10').strip()
# if mw.M('temp_login').insert(pdata): r = TempLogin.query.filter(TempLogin.id == tl_id).delete()
# mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(mw.formatDate(times=pdata['expire']))) db.session.commit()
# return mw.getJson({'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': pdata['expire']}) if r > 0:
return mw.returnData(False, '连接生成失败') mw.writeLog('面板设置', '删除临时登录连接')
return mw.returnData(True, '删除成功')
return mw.returnData(False, '删除失败')
@blueprint.route('/get_temp_login_logs', endpoint='get_temp_login_logs', methods=['POST'])
@panel_login_required
def get_temp_login_logs():
if 'tmp_login_expire' in session:
return mw.returnData(False, '没有权限')
return mw.returnData(False, 'ok', [])

@ -0,0 +1,60 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
import re
import json
import os
from flask import Blueprint, render_template
from flask import request
from admin import model
from admin.user_login_check import panel_login_required
import core.mw as mw
import utils.config as utils_config
from .main import blueprint
# 时区相关
@blueprint.route('/get_timezone_list', endpoint='get_timezone_list', methods=['POST'])
@panel_login_required
def get_timezone_list():
import pytz
# 获取时区列表
# pytz.all_timezones | 所有
# pytz.common_timezones
return pytz.all_timezones
@blueprint.route('/sync_date', endpoint='sync_date', methods=['POST'])
@panel_login_required
def sync_date():
if mw.isAppleSystem():
return mw.returnData(True, '开发系统不必同步时间!')
data = mw.execShell('ntpdate -s time.nist.gov')
if data[0] == '':
return mw.returnData(True, '同步成功!')
return mw.returnData(False, '同步失败:' + data[0])
@blueprint.route('/set_timezone', endpoint='set_timezone', methods=['POST'])
@panel_login_required
def set_timezone():
# 设置时区列表
timezone = request.form.get('timezone', '').strip()
cmd = 'timedatectl set-timezone "'+timezone+'"'
mw.execShell(cmd)
return mw.returnData(True, '设置成功!')

@ -123,6 +123,7 @@ def set_control():
if _day < 1: if _day < 1:
return mw.returnData(False, "设置失败!") return mw.returnData(False, "设置失败!")
option.setOption('monitor_day', day, type='monitor') option.setOption('monitor_day', day, type='monitor')
return mw.returnData(True, "设置成功!")
elif stype == '2': elif stype == '2':
option.setOption('monitor_only_netio', 'close', type='monitor') option.setOption('monitor_only_netio', 'close', type='monitor')
return mw.returnData(True, "设置成功!") return mw.returnData(True, "设置成功!")

@ -576,6 +576,43 @@ def M(table):
sql = db.Sql() sql = db.Sql()
return sql.table(table) return sql.table(table)
def enDoubleCrypt(key, strings):
# 加密字符串
try:
import base64
_key = md5(key).encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes:
strings = strings.encode('utf-8')
import cryptography
from cryptography.fernet import Fernet
f = Fernet(_key)
result = f.encrypt(strings)
return result.decode('utf-8')
except:
writeFileLog(getTracebackInfo())
return strings
def deDoubleCrypt(key, strings):
# 解密字符串
try:
import base64
_key = md5(key).encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes:
strings = strings.encode('utf-8')
from cryptography.fernet import Fernet
f = Fernet(_key)
result = f.decrypt(strings).decode('utf-8')
return result
except:
writeFileLog(getTracebackInfo())
return strings
# ------------------------------ openresty start ----------------------------- # ------------------------------ openresty start -----------------------------
def restartWeb(): def restartWeb():

@ -452,7 +452,7 @@ function setTimezone(){
</div>", </div>",
success:function(){ success:function(){
var tbody = ''; var tbody = '';
$.post('/config/get_timezone_list', {}, function (rdata) { $.post('/setting/get_timezone_list', {}, function (rdata) {
for (var i = 0; i < rdata.length; i++) { for (var i = 0; i < rdata.length; i++) {
if (rdata[i] == 'Asia/Shanghai'){ if (rdata[i] == 'Asia/Shanghai'){
@ -1060,6 +1060,7 @@ function setTempAccessReq(page){
} }
$.post('/setting/get_temp_login', {page:page}, function(rdata){ $.post('/setting/get_temp_login', {page:page}, function(rdata){
console.log(rdata);
if ( typeof(rdata.status) !='undefined' && !rdata.status){ if ( typeof(rdata.status) !='undefined' && !rdata.status){
showMsg(rdata.msg,function(){ showMsg(rdata.msg,function(){
layer.closeAll(); layer.closeAll();
@ -1165,7 +1166,7 @@ function setStatusCode(o){
function setTempAccess(){ function setTempAccess(){
layer.open({ layer.open({
area: ['700px', '250px'], area: ['700px', '380px'],
title: '临时授权管理', title: '临时授权管理',
closeBtn:1, closeBtn:1,
shift: 0, shift: 0,
@ -1268,7 +1269,7 @@ function setAuthBind(){
$('.reset_secret').click(function(){ $('.reset_secret').click(function(){
layer.confirm('您确定要重置当前密钥吗?<br/><span style="color: red; ">重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。</span>',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){ layer.confirm('您确定要重置当前密钥吗?<br/><span style="color: red; ">重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。</span>',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){
}}, function() { }}, function() {
$.post('/config/get_auth_secret', {'reset':"1"},function(rdata){ $.post('/setting/get_auth_secret', {'reset':"1"},function(rdata){
showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){ showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){
$('input[name="secret"]').val(rdata.data['secret']); $('input[name="secret"]').val(rdata.data['secret']);
$('.qrcode').html('').qrcode({ text: rdata.data['url']}); $('.qrcode').html('').qrcode({ text: rdata.data['url']});

@ -117,4 +117,21 @@ class Firewall(object):
data['firewall_status'] = False data['firewall_status'] = False
else: else:
data['firewall_status'] = self.getFwStatus() data['firewall_status'] = self.getFwStatus()
return mw.getJson(data) return mw.getJson(data)
def setPing(self):
if mw.isAppleSystem():
return mw.returnData(True, '开发机不能操作!')
status = request.form.get('status')
filename = '/etc/sysctl.conf'
conf = mw.readFile(filename)
if conf.find('net.ipv4.icmp_echo') != -1:
rep = r"net\.ipv4\.icmp_echo.*"
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf)
else:
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status
mw.writeFile(filename, conf)
mw.execShell('sysctl -p')
return mw.returnData(True, '设置成功!')

@ -549,13 +549,7 @@ class MwPlugin(object):
data = self.getAllPluginList(type,keyword, page, size) data = self.getAllPluginList(type,keyword, page, size)
rdata['data'] = data[0] rdata['data'] = data[0]
rdata['list'] = mw.getPage({'count':data[1],'p':page,'tojs':'getSList','row':size})
args = {}
args['count'] = data[1]
args['p'] = page
args['tojs'] = 'getSList'
args['row'] = size
rdata['list'] = mw.getPage(args)
return rdata return rdata
# shell/bash方式调用 # shell/bash方式调用

Loading…
Cancel
Save