mirror of https://github.com/jumpserver/jumpserver
[Update] 添加用户登录失败次数限制
parent
43370c547a
commit
ff9b1a887f
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-07-03 16:48+0800\n"
|
"POT-Creation-Date: 2018-07-04 16:46+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
||||||
|
@ -156,7 +156,7 @@ msgstr "名称"
|
||||||
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13
|
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13
|
||||||
#: users/forms.py:31 users/models/authentication.py:70 users/models/user.py:47
|
#: users/forms.py:31 users/models/authentication.py:70 users/models/user.py:47
|
||||||
#: users/templates/users/_select_user_modal.html:14
|
#: users/templates/users/_select_user_modal.html:14
|
||||||
#: users/templates/users/login.html:56
|
#: users/templates/users/login.html:60
|
||||||
#: users/templates/users/login_log_list.html:49
|
#: users/templates/users/login_log_list.html:49
|
||||||
#: users/templates/users/user_detail.html:67
|
#: users/templates/users/user_detail.html:67
|
||||||
#: users/templates/users/user_list.html:24
|
#: users/templates/users/user_list.html:24
|
||||||
|
@ -170,7 +170,7 @@ msgstr "密码或密钥密码"
|
||||||
|
|
||||||
#: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113
|
#: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113
|
||||||
#: users/forms.py:15 users/forms.py:33 users/forms.py:45
|
#: users/forms.py:15 users/forms.py:33 users/forms.py:45
|
||||||
#: users/templates/users/login.html:59
|
#: users/templates/users/login.html:63
|
||||||
#: users/templates/users/reset_password.html:53
|
#: users/templates/users/reset_password.html:53
|
||||||
#: users/templates/users/user_create.html:10
|
#: users/templates/users/user_create.html:10
|
||||||
#: users/templates/users/user_password_authentication.html:14
|
#: users/templates/users/user_password_authentication.html:14
|
||||||
|
@ -2006,7 +2006,7 @@ msgid "Logout"
|
||||||
msgstr "注销登录"
|
msgstr "注销登录"
|
||||||
|
|
||||||
#: templates/_header_bar.html:49 users/templates/users/login.html:44
|
#: templates/_header_bar.html:49 users/templates/users/login.html:44
|
||||||
#: users/templates/users/login.html:64
|
#: users/templates/users/login.html:68
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "登录"
|
msgstr "登录"
|
||||||
|
|
||||||
|
@ -2046,7 +2046,7 @@ msgstr "关闭"
|
||||||
|
|
||||||
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
|
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
|
||||||
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
|
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
|
||||||
#: users/views/login.py:311 users/views/login.py:369 users/views/user.py:65
|
#: users/views/login.py:330 users/views/login.py:388 users/views/user.py:65
|
||||||
#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175
|
#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175
|
||||||
#: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415
|
#: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
|
@ -2641,7 +2641,7 @@ msgid " for more information"
|
||||||
msgstr "获取更多信息"
|
msgstr "获取更多信息"
|
||||||
|
|
||||||
#: users/templates/users/forgot_password.html:26
|
#: users/templates/users/forgot_password.html:26
|
||||||
#: users/templates/users/login.html:73
|
#: users/templates/users/login.html:77
|
||||||
msgid "Forgot password"
|
msgid "Forgot password"
|
||||||
msgstr "忘记密码"
|
msgstr "忘记密码"
|
||||||
|
|
||||||
|
@ -2650,6 +2650,10 @@ msgid "Input your email, that will send a mail to your"
|
||||||
msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
|
msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
|
||||||
|
|
||||||
#: users/templates/users/login.html:50
|
#: users/templates/users/login.html:50
|
||||||
|
msgid "Log in frequently and try again later"
|
||||||
|
msgstr "登录频繁, 稍后重试"
|
||||||
|
|
||||||
|
#: users/templates/users/login.html:53
|
||||||
msgid "Captcha invalid"
|
msgid "Captcha invalid"
|
||||||
msgstr "验证码错误"
|
msgstr "验证码错误"
|
||||||
|
|
||||||
|
@ -3045,60 +3049,60 @@ msgstr "更新用户组"
|
||||||
msgid "User group granted asset"
|
msgid "User group granted asset"
|
||||||
msgstr "用户组授权资产"
|
msgstr "用户组授权资产"
|
||||||
|
|
||||||
#: users/views/login.py:63
|
#: users/views/login.py:74
|
||||||
msgid "Please enable cookies and try again."
|
msgid "Please enable cookies and try again."
|
||||||
msgstr "设置你的浏览器支持cookie"
|
msgstr "设置你的浏览器支持cookie"
|
||||||
|
|
||||||
#: users/views/login.py:159 users/views/user.py:500 users/views/user.py:525
|
#: users/views/login.py:178 users/views/user.py:500 users/views/user.py:525
|
||||||
msgid "MFA code invalid"
|
msgid "MFA code invalid"
|
||||||
msgstr "MFA码认证失败"
|
msgstr "MFA码认证失败"
|
||||||
|
|
||||||
#: users/views/login.py:188
|
#: users/views/login.py:207
|
||||||
msgid "Logout success"
|
msgid "Logout success"
|
||||||
msgstr "退出登录成功"
|
msgstr "退出登录成功"
|
||||||
|
|
||||||
#: users/views/login.py:189
|
#: users/views/login.py:208
|
||||||
msgid "Logout success, return login page"
|
msgid "Logout success, return login page"
|
||||||
msgstr "退出登录成功,返回到登录页面"
|
msgstr "退出登录成功,返回到登录页面"
|
||||||
|
|
||||||
#: users/views/login.py:205
|
#: users/views/login.py:224
|
||||||
msgid "Email address invalid, please input again"
|
msgid "Email address invalid, please input again"
|
||||||
msgstr "邮箱地址错误,重新输入"
|
msgstr "邮箱地址错误,重新输入"
|
||||||
|
|
||||||
#: users/views/login.py:218
|
#: users/views/login.py:237
|
||||||
msgid "Send reset password message"
|
msgid "Send reset password message"
|
||||||
msgstr "发送重置密码邮件"
|
msgstr "发送重置密码邮件"
|
||||||
|
|
||||||
#: users/views/login.py:219
|
#: users/views/login.py:238
|
||||||
msgid "Send reset password mail success, login your mail box and follow it "
|
msgid "Send reset password mail success, login your mail box and follow it "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
|
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
|
||||||
|
|
||||||
#: users/views/login.py:232
|
#: users/views/login.py:251
|
||||||
msgid "Reset password success"
|
msgid "Reset password success"
|
||||||
msgstr "重置密码成功"
|
msgstr "重置密码成功"
|
||||||
|
|
||||||
#: users/views/login.py:233
|
#: users/views/login.py:252
|
||||||
msgid "Reset password success, return to login page"
|
msgid "Reset password success, return to login page"
|
||||||
msgstr "重置密码成功,返回到登录页面"
|
msgstr "重置密码成功,返回到登录页面"
|
||||||
|
|
||||||
#: users/views/login.py:254 users/views/login.py:267
|
#: users/views/login.py:273 users/views/login.py:286
|
||||||
msgid "Token invalid or expired"
|
msgid "Token invalid or expired"
|
||||||
msgstr "Token错误或失效"
|
msgstr "Token错误或失效"
|
||||||
|
|
||||||
#: users/views/login.py:263
|
#: users/views/login.py:282
|
||||||
msgid "Password not same"
|
msgid "Password not same"
|
||||||
msgstr "密码不一致"
|
msgstr "密码不一致"
|
||||||
|
|
||||||
#: users/views/login.py:273 users/views/user.py:118 users/views/user.py:398
|
#: users/views/login.py:292 users/views/user.py:118 users/views/user.py:398
|
||||||
msgid "* Your password does not meet the requirements"
|
msgid "* Your password does not meet the requirements"
|
||||||
msgstr "* 您的密码不符合要求"
|
msgstr "* 您的密码不符合要求"
|
||||||
|
|
||||||
#: users/views/login.py:311
|
#: users/views/login.py:330
|
||||||
msgid "First login"
|
msgid "First login"
|
||||||
msgstr "首次登陆"
|
msgstr "首次登陆"
|
||||||
|
|
||||||
#: users/views/login.py:370
|
#: users/views/login.py:389
|
||||||
msgid "Login log list"
|
msgid "Login log list"
|
||||||
msgstr "登录日志"
|
msgstr "登录日志"
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,17 @@
|
||||||
</div>
|
</div>
|
||||||
<form class="m-t" role="form" method="post" action="">
|
<form class="m-t" role="form" method="post" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if form.errors %}
|
|
||||||
|
{% if login_limit %}
|
||||||
|
<p class="red-fonts">{% trans 'Log in frequently and try again later' %}</p>
|
||||||
|
{% elif form.errors %}
|
||||||
{% if 'captcha' in form.errors %}
|
{% if 'captcha' in form.errors %}
|
||||||
<p class="red-fonts">{% trans 'Captcha invalid' %}</p>
|
<p class="red-fonts">{% trans 'Captcha invalid' %}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="red-fonts">{{ form.non_field_errors.as_text }}</p>
|
<p class="red-fonts">{{ form.non_field_errors.as_text }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="{{ form.username.html_name }}" placeholder="{% trans 'Username' %}" required="" value="{% if form.username.value %}{{ form.username.value }}{% endif %}">
|
<input type="text" class="form-control" name="{{ form.username.html_name }}" placeholder="{% trans 'Username' %}" required="" value="{% if form.username.value %}{{ form.username.value }}{% endif %}">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -48,7 +48,8 @@ class UserLoginView(FormView):
|
||||||
form_class = forms.UserLoginForm
|
form_class = forms.UserLoginForm
|
||||||
form_class_captcha = forms.UserLoginCaptchaForm
|
form_class_captcha = forms.UserLoginCaptchaForm
|
||||||
redirect_field_name = 'next'
|
redirect_field_name = 'next'
|
||||||
key_prefix = "_LOGIN_INVALID_{}"
|
key_prefix_captcha = "_LOGIN_INVALID_{}"
|
||||||
|
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if request.user.is_staff:
|
if request.user.is_staff:
|
||||||
|
@ -58,6 +59,16 @@ class UserLoginView(FormView):
|
||||||
request.session.set_test_cookie()
|
request.session.set_test_cookie()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
# limit login authentication
|
||||||
|
ip = get_login_ip(request)
|
||||||
|
username = self.request.POST.get('username')
|
||||||
|
count = cache.get(self.key_prefix_limit.format(ip, username))
|
||||||
|
if count and count >= 3:
|
||||||
|
return self.render_to_response(self.get_context_data(login_limit=True))
|
||||||
|
|
||||||
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if not self.request.session.test_cookie_worked():
|
if not self.request.session.test_cookie_worked():
|
||||||
return HttpResponse(_("Please enable cookies and try again."))
|
return HttpResponse(_("Please enable cookies and try again."))
|
||||||
|
@ -66,17 +77,24 @@ class UserLoginView(FormView):
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
# Write login failed log
|
# write login failed log
|
||||||
|
username = form.cleaned_data.get('username')
|
||||||
data = {
|
data = {
|
||||||
'username': form.cleaned_data.get('username'),
|
'username': username,
|
||||||
'mfa': LoginLog.MFA_UNKNOWN,
|
'mfa': LoginLog.MFA_UNKNOWN,
|
||||||
'reason': LoginLog.REASON_PASSWORD,
|
'reason': LoginLog.REASON_PASSWORD,
|
||||||
'status': False
|
'status': False
|
||||||
}
|
}
|
||||||
self.write_login_log(data)
|
self.write_login_log(data)
|
||||||
|
|
||||||
|
# limit user login failed times
|
||||||
ip = get_login_ip(self.request)
|
ip = get_login_ip(self.request)
|
||||||
cache.set(self.key_prefix.format(ip), 1, 3600)
|
key_limit = self.key_prefix_limit.format(ip, username)
|
||||||
|
count = cache.get(key_limit)
|
||||||
|
count = count + 1 if count else 1
|
||||||
|
cache.set(key_limit, count, 1800)
|
||||||
|
|
||||||
|
cache.set(self.key_prefix_captcha.format(ip), 1, 3600)
|
||||||
old_form = form
|
old_form = form
|
||||||
form = self.form_class_captcha(data=form.data)
|
form = self.form_class_captcha(data=form.data)
|
||||||
form._errors = old_form.errors
|
form._errors = old_form.errors
|
||||||
|
@ -84,7 +102,7 @@ class UserLoginView(FormView):
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
ip = get_login_ip(self.request)
|
ip = get_login_ip(self.request)
|
||||||
if cache.get(self.key_prefix.format(ip)):
|
if cache.get(self.key_prefix_captcha.format(ip)):
|
||||||
return self.form_class_captcha
|
return self.form_class_captcha
|
||||||
else:
|
else:
|
||||||
return self.form_class
|
return self.form_class
|
||||||
|
@ -101,7 +119,6 @@ class UserLoginView(FormView):
|
||||||
elif not user.otp_enabled:
|
elif not user.otp_enabled:
|
||||||
# 0 & T,F
|
# 0 & T,F
|
||||||
auth_login(self.request, user)
|
auth_login(self.request, user)
|
||||||
# Write login success log
|
|
||||||
data = {
|
data = {
|
||||||
'username': self.request.user.username,
|
'username': self.request.user.username,
|
||||||
'mfa': int(self.request.user.otp_enabled),
|
'mfa': int(self.request.user.otp_enabled),
|
||||||
|
@ -142,7 +159,6 @@ class UserLoginOtpView(FormView):
|
||||||
|
|
||||||
if check_otp_code(otp_secret_key, otp_code):
|
if check_otp_code(otp_secret_key, otp_code):
|
||||||
auth_login(self.request, user)
|
auth_login(self.request, user)
|
||||||
# Write login success log
|
|
||||||
data = {
|
data = {
|
||||||
'username': self.request.user.username,
|
'username': self.request.user.username,
|
||||||
'mfa': int(self.request.user.otp_enabled),
|
'mfa': int(self.request.user.otp_enabled),
|
||||||
|
@ -152,7 +168,6 @@ class UserLoginOtpView(FormView):
|
||||||
self.write_login_log(data)
|
self.write_login_log(data)
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
else:
|
else:
|
||||||
# Write login failed log
|
|
||||||
data = {
|
data = {
|
||||||
'username': user.username,
|
'username': user.username,
|
||||||
'mfa': int(user.otp_enabled),
|
'mfa': int(user.otp_enabled),
|
||||||
|
|
Loading…
Reference in New Issue