diff --git a/server/www/teleport/static/js/asset/host-list.js b/server/www/teleport/static/js/asset/host-list.js
index 4a9ce98..96729fe 100644
--- a/server/www/teleport/static/js/asset/host-list.js
+++ b/server/www/teleport/static/js/asset/host-list.js
@@ -829,6 +829,7 @@ $app.create_dlg_edit_host = function () {
dlg.dom.edit_router_port.val('' + dlg.field_router_port);
}
} else {
+ dlg.field_router_ip = '';
dlg.field_router_port = 0;
}
@@ -878,88 +879,6 @@ $app.create_dlg_edit_host = function () {
return dlg;
};
-$app.create_dlg_host_info = function () {
- var dlg = {};
- dlg.dom_id = 'dlg-user-info';
- dlg.row_id = -1;
- dlg.need_edit = false;
-
- dlg.dom = {
- dialog: $('#' + dlg.dom_id),
- dlg_title: $('#' + dlg.dom_id + ' [data-field="dlg-title"]'),
- info: $('#' + dlg.dom_id + ' [data-field="user-info"]'),
- btn_edit: $('#' + dlg.dom_id + ' [data-field="btn-edit"]')
- };
-
- dlg.init = function (cb_stack) {
- dlg.dom.dialog.on('hidden.bs.modal', function () {
- if (!dlg.need_edit)
- return;
- $app.dlg_edit_user.show_edit(dlg.row_id);
- });
-
- dlg.dom.btn_edit.click(function () {
- dlg.need_edit = true;
- dlg.dom.dialog.modal('hide');
- });
-
- cb_stack.exec();
- };
-
- dlg.show = function (row_id) {
- dlg.row_id = row_id;
- dlg.need_edit = false;
-
- var _row_data = $app.table_host.get_row(dlg.row_id);
-
- // 表格加载时,是不会读取用户的 desc 字段的,因此可以判断此用户是否已经读取过详细信息了
- if (_.isUndefined(_row_data.desc)) {
- // 尚未读取,则向服务器要求获取此用户账号的完整信息
- $tp.ajax_post_json('/user/get-user/' + _row_data.id, {},
- function (ret) {
- if (ret.code === TPE_OK) {
- $app.table_host.update_row(dlg.row_id, ret.data);
- dlg.show_info(ret.data);
- } else {
- $tp.notify_error('无法获取用户详细信息:' + tp_error_msg(ret.code, ret.message));
- }
- },
- function () {
- $tp.notify_error('网络故障,无法获取用户详细信息!');
- }
- );
- } else {
- dlg.show_info(_row_data);
- }
- };
-
- dlg.show_info = function (user) {
- // 更新对话框中显示的信息
- dlg.dom.dlg_title.html(' ' + user.surname);
-
- var info = [];
-
- var not_set = '未设置';
- var mobile = (user.mobile.length === 0) ? not_set : user.mobile;
- var qq = (user.qq.length === 0) ? not_set : user.qq;
- var wechat = (user.wechat.length === 0) ? not_set : user.wechat;
- var desc = (user.desc.length === 0) ? not_set : user.desc;
- info.push('
账号: | ' + user.username + ' |
');
- info.push('姓名: | ' + user.surname + ' |
');
- info.push('邮箱: | ' + user.email + ' |
');
- info.push('电话: | ' + mobile + ' |
');
- info.push('QQ: | ' + qq + ' |
');
- info.push('微信: | ' + wechat + ' |
');
- info.push('描述: | ' + desc + ' |
');
-
- dlg.dom.info.html($(info.join('')));
-
- dlg.dom.dialog.modal();
- };
-
- return dlg;
-};
-
$app.create_dlg_accounts = function () {
var dlg = {};
dlg.dom_id = 'dlg-accounts';
diff --git a/server/www/teleport/webroot/app/base/configs.py b/server/www/teleport/webroot/app/base/configs.py
index aab246a..5f3b3e4 100644
--- a/server/www/teleport/webroot/app/base/configs.py
+++ b/server/www/teleport/webroot/app/base/configs.py
@@ -486,7 +486,7 @@ class AppConfig(BaseAppConfig):
self.sys.login = tp_convert_to_attr_dict(_login)
if not self.sys.login.is_exists('session_timeout'):
- self.sys.login.session_timeout = 30
+ self.sys.login.session_timeout = 60 # 1 hour
if not self.sys.login.is_exists('retry'):
self.sys.login.retry = 0
if not self.sys.login.is_exists('lock_timeout'):
diff --git a/server/www/teleport/webroot/app/base/session.py b/server/www/teleport/webroot/app/base/session.py
index de66470..e5bc039 100644
--- a/server/www/teleport/webroot/app/base/session.py
+++ b/server/www/teleport/webroot/app/base/session.py
@@ -4,6 +4,7 @@ import datetime
import threading
from app.base.logger import log
+from app.base.configs import get_cfg
class SessionManager(threading.Thread):
@@ -62,7 +63,8 @@ class SessionManager(threading.Thread):
"""
if expire is None:
- expire = self.SESSION_EXPIRE
+ # expire = self.SESSION_EXPIRE
+ expire = get_cfg().sys.login.session_timeout * 60
if expire < 0:
with self._lock:
diff --git a/server/www/teleport/webroot/app/const.py b/server/www/teleport/webroot/app/const.py
index 2ed30d9..8e67f20 100644
--- a/server/www/teleport/webroot/app/const.py
+++ b/server/www/teleport/webroot/app/const.py
@@ -9,13 +9,6 @@ TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH = 0x0008 # 用户名+密码+OATH
APP_MODE_NORMAL = 1
APP_MODE_MAINTENANCE = 2
-# ==========================================================================
-# 用户账号状态
-# ==========================================================================
-USER_STATE_NORMAL = 1 # 正常状态
-USER_STATE_LOCKED = 2 # 锁定(例如密码连错n次)
-USER_STATE_DISABLED = 3 # 禁用
-
# ==========================================================================
# 远程主机账号认证方式
# ==========================================================================
diff --git a/server/www/teleport/webroot/app/controller/auth.py b/server/www/teleport/webroot/app/controller/auth.py
index f095da7..cdb2815 100644
--- a/server/www/teleport/webroot/app/controller/auth.py
+++ b/server/www/teleport/webroot/app/controller/auth.py
@@ -11,6 +11,7 @@ from app.logic.auth.captcha import tp_captcha_generate_image
from app.model import user
from app.model import syslog
from app.logic.auth.password import tp_password_verify
+from app.base.utils import tp_timestamp_utc_now
class LoginHandler(TPBaseHandler):
@@ -53,68 +54,86 @@ class LoginHandler(TPBaseHandler):
class DoLoginHandler(TPBaseJsonHandler):
def post(self):
+ sys_cfg = get_cfg().sys
+
args = self.get_argument('args', None)
- if args is not None:
- try:
- args = json.loads(args)
- except:
- return self.write_json(TPE_JSON_FORMAT, '参数错误')
+ if args is None:
+ return self.write_json(TPE_PARAM)
- try:
- login_type = args['type']
- captcha = args['captcha'].strip()
- username = args['username'].strip()
- password = args['password']
- oath = args['oath'].strip()
- remember = args['remember']
- except:
- return self.write_json(TPE_PARAM, '参数错误')
- else:
- return self.write_json(TPE_PARAM, '参数错误')
+ try:
+ args = json.loads(args)
+ except:
+ return self.write_json(TPE_JSON_FORMAT, '参数错误')
- _tmp = {'username': username, 'surname': username}
+ try:
+ login_type = args['type']
+ captcha = args['captcha'].strip()
+ username = args['username'].strip().lower()
+ password = args['password']
+ oath = args['oath'].strip()
+ remember = args['remember']
+ except:
+ return self.write_json(TPE_PARAM)
+
+ if login_type not in [TP_LOGIN_AUTH_USERNAME_PASSWORD,
+ TP_LOGIN_AUTH_USERNAME_PASSWORD_CAPTCHA,
+ TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH,
+ TP_LOGIN_AUTH_USERNAME_OATH
+ ]:
+ return self.write_json(TPE_PARAM, '未知的认证方式')
if login_type == TP_LOGIN_AUTH_USERNAME_PASSWORD_CAPTCHA:
oath = None
code = self.get_session('captcha')
if code is None:
return self.write_json(TPE_CAPTCHA_EXPIRED, '验证码已失效')
- self.del_session('captcha')
if code.lower() != captcha.lower():
return self.write_json(TPE_CAPTCHA_MISMATCH, '验证码错误')
- elif login_type == TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH:
+ elif login_type in [TP_LOGIN_AUTH_USERNAME_OATH, TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH]:
if len(oath) == 0:
return self.write_json(TPE_OATH_MISMATCH, '未提供身份验证器动态验证码')
- else:
- return self.write_json(TPE_PARAM, '参数错误')
self.del_session('captcha')
+ if len(username) == 0:
+ return self.write_json(TPE_PARAM, '未提供登录用户名')
+
err, user_info = user.get_by_username(username)
- # if user_info is None:
- # return self.write_json(TPE_USER_AUTH)
if err != TPE_OK:
if err == TPE_NOT_EXISTS:
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_NOT_EXISTS, '登录失败,用户`{}`不存在'.format(username))
+ syslog.sys_log({'username': username, 'surname': username}, self.request.remote_ip, TPE_NOT_EXISTS, '登录失败,用户`{}`不存在'.format(username))
return self.write_json(err)
- if user_info['state'] == USER_STATE_LOCKED:
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_USER_LOCKED, '登录失败,用户已被锁定')
- return self.write_json(TPE_USER_LOCKED)
- elif user_info['state'] == USER_STATE_DISABLED:
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_USER_DISABLED, '登录失败,用户已被禁用')
+ if user_info['state'] == TP_STATE_LOCKED:
+ # 用户已经被锁定,如果系统配置为一定时间后自动解锁,则更新一下用户信息
+ if sys_cfg.login.lock_timeout != 0:
+ if tp_timestamp_utc_now() - user_info.lock_time > sys_cfg.login.lock_timeout * 60:
+ user_info.fail_count = 0
+ user_info.state = TP_STATE_NORMAL
+ if user_info['state'] == TP_STATE_LOCKED:
+ syslog.sys_log(user_info, self.request.remote_ip, TPE_USER_LOCKED, '登录失败,用户已被锁定')
+ return self.write_json(TPE_USER_LOCKED)
+ elif user_info['state'] == TP_STATE_DISABLED:
+ syslog.sys_log(user_info, self.request.remote_ip, TPE_USER_DISABLED, '登录失败,用户已被禁用')
return self.write_json(TPE_USER_DISABLED)
- elif user_info['state'] != USER_STATE_NORMAL:
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_FAILED, '登录失败,系统内部错误')
+ elif user_info['state'] != TP_STATE_NORMAL:
+ syslog.sys_log(user_info, self.request.remote_ip, TPE_FAILED, '登录失败,系统内部错误')
return self.write_json(TPE_FAILED)
- if login_type == TP_LOGIN_AUTH_USERNAME_PASSWORD_CAPTCHA:
+ err_msg = ''
+ if login_type in [TP_LOGIN_AUTH_USERNAME_PASSWORD, TP_LOGIN_AUTH_USERNAME_PASSWORD_CAPTCHA, TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH]:
if not tp_password_verify(password, user_info['password']):
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_USER_AUTH, '登录失败,密码错误!')
+ err, is_locked = user.update_fail_count(self, user_info)
+ if is_locked:
+ err_msg = '用户被临时锁定!'
+ syslog.sys_log(user_info, self.request.remote_ip, TPE_USER_AUTH, '登录失败,密码错误!{}'.format(err_msg))
return self.write_json(TPE_USER_AUTH)
- elif login_type == TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH:
+ elif login_type in [TP_LOGIN_AUTH_USERNAME_OATH, TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH]:
if not tp_oath_verify_code(user_info['oath_secret'], oath):
- syslog.sys_log(_tmp, self.request.remote_ip, TPE_OATH_MISMATCH, "登录失败,身份验证器动态验证码错误!")
+ err, is_locked = user.update_fail_count(self, user_info)
+ if is_locked:
+ err_msg = '用户被临时锁定!'
+ syslog.sys_log(user_info, self.request.remote_ip, TPE_OATH_MISMATCH, "登录失败,身份验证器动态验证码错误!{}".format(err_msg))
return self.write_json(TPE_OATH_MISMATCH)
self._user = user_info
@@ -122,12 +141,9 @@ class DoLoginHandler(TPBaseJsonHandler):
del self._user['password']
del self._user['oath_secret']
- print('00000', self._user)
-
if remember:
self.set_session('user', self._user, 12 * 60 * 60)
else:
- # TODO: 使用系统配置项中的默认会话超时
self.set_session('user', self._user)
user.update_login_info(self, user_info['id'])
diff --git a/server/www/teleport/webroot/app/model/user.py b/server/www/teleport/webroot/app/model/user.py
index b5c13e0..cc4dafb 100644
--- a/server/www/teleport/webroot/app/model/user.py
+++ b/server/www/teleport/webroot/app/model/user.py
@@ -2,14 +2,12 @@
# import hashlib
-from app.const import *
-from app.base.logger import log
-# from app.base.configs import get_cfg
+from app.base.configs import get_cfg
from app.base.db import get_db, SQL
-from app.model import syslog
-# from app.logic.auth.oath import tp_oath_verify_code
-# from app.logic.auth.password import tp_password_generate_secret, tp_password_verify
+from app.base.logger import log
from app.base.utils import tp_timestamp_utc_now
+from app.const import *
+from app.model import syslog
def get_user_info(user_id):
@@ -32,7 +30,7 @@ def get_user_info(user_id):
def get_by_username(username):
s = SQL(get_db())
- s.select_from('user', ['id', 'type', 'auth_type', 'username', 'surname', 'password', 'oath_secret', 'role_id', 'state', 'email', 'create_time', 'last_login', 'last_ip', 'last_chpass', 'mobile', 'qq', 'wechat', 'desc'], alt_name='u')
+ s.select_from('user', ['id', 'type', 'auth_type', 'username', 'surname', 'password', 'oath_secret', 'role_id', 'state', 'fail_count', 'lock_time', 'email', 'create_time', 'last_login', 'last_ip', 'last_chpass', 'mobile', 'qq', 'wechat', 'desc'], alt_name='u')
s.left_join('role', ['name', 'privilege'], join_on='r.id=u.role_id', alt_name='r', out_map={'name': 'role'})
s.where('u.username="{}"'.format(username))
err = s.query()
@@ -249,11 +247,10 @@ def set_password(handler, user_id, password):
def update_login_info(handler, user_id):
-
db = get_db()
_time_now = tp_timestamp_utc_now()
- sql = 'UPDATE `{}user` SET last_login=login_time, last_ip=login_ip, login_time={login_time}, login_ip="{ip}" WHERE id={user_id};' \
+ sql = 'UPDATE `{}user` SET fail_count=0, last_login=login_time, last_ip=login_ip, login_time={login_time}, login_ip="{ip}" WHERE id={user_id};' \
''.format(db.table_prefix,
login_time=_time_now, ip=handler.request.remote_ip, user_id=user_id
)
@@ -285,6 +282,29 @@ def update_users_state(handler, user_ids, state):
return TPE_DATABASE
+def update_fail_count(handler, user_info):
+ db = get_db()
+ sys_cfg = get_cfg().sys
+ sql_list = []
+ is_locked = False
+ fail_count = user_info.fail_count + 1
+
+ sql = 'UPDATE `{}user` SET fail_count={count} WHERE id={uid};' \
+ ''.format(db.table_prefix, count=fail_count, uid=user_info.id)
+ sql_list.append(sql)
+
+ if sys_cfg.login.retry != 0 and fail_count >= sys_cfg.login.retry:
+ is_locked = True
+ sql = 'UPDATE `{}user` SET state={state}, lock_time={lock_time} WHERE id={uid};' \
+ ''.format(db.table_prefix, state=TP_STATE_LOCKED, lock_time=tp_timestamp_utc_now(), uid=user_info.id)
+ sql_list.append(sql)
+
+ if db.transaction(sql_list):
+ return TPE_OK, is_locked
+ else:
+ return TPE_DATABASE, is_locked
+
+
def remove_users(handler, users):
s = SQL(get_db())
@@ -587,7 +607,7 @@ def get_group_with_member(sql_filter, sql_order, sql_limit):
for g in sg.recorder:
g['member_count'] = 0
g['members'] = []
- g['_mid'] = [] # 临时使用,构建此组的前5个成员的id
+ g['_mid'] = [] # 临时使用,构建此组的前5个成员的id
# 对于本次要返回的用户组,取其中每一个组内成员的基本信息(id/用户名/真实名称等)
groups = [g['id'] for g in sg.recorder]