mirror of https://github.com/jumpserver/jumpserver
[Update] 添加解除用户登录限制功能
parent
1182313c1a
commit
812078331e
Binary file not shown.
|
@ -2774,11 +2774,11 @@ msgstr "发送重置密钥邮件"
|
|||
#: users/templates/users/user_detail.html:186
|
||||
#: users/templates/users/user_detail.html:444
|
||||
msgid "Unblock user"
|
||||
msgstr "解锁用户"
|
||||
msgstr "解除登录限制"
|
||||
|
||||
#: users/templates/users/user_detail.html:189
|
||||
msgid "Unblock"
|
||||
msgstr "解锁"
|
||||
msgstr "解除"
|
||||
|
||||
#: users/templates/users/user_detail.html:303
|
||||
msgid "Goto profile page enable MFA"
|
||||
|
@ -2820,7 +2820,7 @@ msgstr "ssh密钥"
|
|||
|
||||
#: users/templates/users/user_detail.html:454
|
||||
msgid "After unlocking the user, the user can log in normally."
|
||||
msgstr "解锁用户后,此用户即可正常登录"
|
||||
msgstr "解除用户登录限制后,此用户即可正常登录"
|
||||
|
||||
#: users/templates/users/user_group_create_update.html:31
|
||||
msgid "Cancel"
|
||||
|
@ -3181,7 +3181,7 @@ msgid "MFA disable success, return login page"
|
|||
msgstr "MFA 解绑成功,返回登录页面"
|
||||
|
||||
#~ msgid "Unblock user successfully. "
|
||||
#~ msgstr "解锁用户成功"
|
||||
#~ msgstr "解除登录限制成功"
|
||||
|
||||
#~ msgid "Clear"
|
||||
#~ msgstr "清除"
|
||||
|
|
|
@ -100,12 +100,15 @@ class UserUnblockPKApi(generics.UpdateAPIView):
|
|||
permission_classes = (IsSuperUser,)
|
||||
serializer_class = UserSerializer
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def perform_update(self, serializer):
|
||||
user = self.get_object()
|
||||
username = user.username if user else ''
|
||||
key_limit = self.key_prefix_limit.format(username, '*')
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
cache.delete_pattern(key_limit)
|
||||
cache.delete(key_block)
|
||||
|
||||
|
||||
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||
|
@ -210,6 +213,7 @@ class UserAuthApi(APIView):
|
|||
permission_classes = (AllowAny,)
|
||||
serializer_class = UserSerializer
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def post(self, request):
|
||||
# limit login
|
||||
|
@ -217,6 +221,7 @@ class UserAuthApi(APIView):
|
|||
ip = request.data.get('remote_addr', None)
|
||||
ip = ip if ip else get_login_ip(request)
|
||||
key_limit = self.key_prefix_limit.format(username, ip)
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
if is_block_login(key_limit):
|
||||
msg = _("Log in frequently and try again later")
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
@ -231,7 +236,7 @@ class UserAuthApi(APIView):
|
|||
}
|
||||
self.write_login_log(request, data)
|
||||
|
||||
set_user_login_failed_count_to_cache(key_limit)
|
||||
set_user_login_failed_count_to_cache(key_limit, key_block)
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
||||
if not user.otp_enabled:
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="id_unblock_user" style="{% if not unblock %}display:none{% endif %}">
|
||||
<td>{% trans 'Unblock user' %}</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
|
@ -283,7 +283,7 @@ $(document).ready(function() {
|
|||
.on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.nodes_selected[data.id];
|
||||
})
|
||||
});
|
||||
})
|
||||
.on('click', '#is_active', function() {
|
||||
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
|
||||
|
@ -301,7 +301,7 @@ $(document).ready(function() {
|
|||
.on('click', '#force_enable_otp', function() {
|
||||
{% if request.user == user_object %}
|
||||
toastr.error("{% trans 'Goto profile page enable MFA' %}");
|
||||
return
|
||||
return;
|
||||
{% endif %}
|
||||
|
||||
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
|
||||
|
@ -441,7 +441,15 @@ $(document).ready(function() {
|
|||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans "Success" %}";
|
||||
swal("{% trans 'Unblock user' %}", msg, "success");
|
||||
{#swal("{% trans 'Unblock user' %}", msg, "success");#}
|
||||
swal({
|
||||
title: "{% trans 'Unblock user' %}",
|
||||
text: msg,
|
||||
type: "success"
|
||||
}, function() {
|
||||
location.reload()
|
||||
}
|
||||
);
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
|
@ -460,7 +468,6 @@ $(document).ready(function() {
|
|||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -333,7 +333,7 @@ def check_password_rules(password):
|
|||
return bool(match_obj)
|
||||
|
||||
|
||||
def set_user_login_failed_count_to_cache(key_limit):
|
||||
def set_user_login_failed_count_to_cache(key_limit, key_block):
|
||||
count = cache.get(key_limit)
|
||||
count = count + 1 if count else 1
|
||||
|
||||
|
@ -343,6 +343,15 @@ def set_user_login_failed_count_to_cache(key_limit):
|
|||
limit_time = setting_limit_time.cleaned_value if setting_limit_time \
|
||||
else settings.DEFAULT_LOGIN_LIMIT_TIME
|
||||
|
||||
setting_limit_count = Setting.objects.filter(
|
||||
name='SECURITY_LOGIN_LIMIT_COUNT'
|
||||
).first()
|
||||
limit_count = setting_limit_count.cleaned_value if setting_limit_count \
|
||||
else settings.DEFAULT_LOGIN_LIMIT_COUNT
|
||||
|
||||
if count >= limit_count:
|
||||
cache.set(key_block, 1, int(limit_time)*60)
|
||||
|
||||
cache.set(key_limit, count, int(limit_time)*60)
|
||||
|
||||
|
||||
|
@ -357,3 +366,9 @@ def is_block_login(key_limit):
|
|||
|
||||
if count and count >= limit_count:
|
||||
return True
|
||||
|
||||
|
||||
def is_need_unblock(key_block):
|
||||
if not cache.get(key_block):
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -51,6 +51,7 @@ class UserLoginView(FormView):
|
|||
redirect_field_name = 'next'
|
||||
key_prefix_captcha = "_LOGIN_INVALID_{}"
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_staff:
|
||||
|
@ -91,7 +92,8 @@ class UserLoginView(FormView):
|
|||
# limit user login failed count
|
||||
ip = get_login_ip(self.request)
|
||||
key_limit = self.key_prefix_limit.format(username, ip)
|
||||
set_user_login_failed_count_to_cache(key_limit)
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
set_user_login_failed_count_to_cache(key_limit, key_block)
|
||||
|
||||
# show captcha
|
||||
cache.set(self.key_prefix_captcha.format(ip), 1, 3600)
|
||||
|
|
|
@ -36,7 +36,9 @@ from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen
|
|||
from common.models import Setting
|
||||
from .. import forms
|
||||
from ..models import User, UserGroup
|
||||
from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, get_user_or_tmp_user, get_password_check_rules, check_password_rules
|
||||
from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, \
|
||||
get_user_or_tmp_user, get_password_check_rules, check_password_rules, \
|
||||
is_need_unblock
|
||||
from ..signals import post_user_create
|
||||
from ..tasks import write_login_log_async
|
||||
|
||||
|
@ -168,13 +170,17 @@ class UserDetailView(AdminUserRequiredMixin, DetailView):
|
|||
model = User
|
||||
template_name = 'users/user_detail.html'
|
||||
context_object_name = "user_object"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
user = self.get_object()
|
||||
key_block = self.key_prefix_block.format(user.username)
|
||||
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('User detail'),
|
||||
'groups': groups
|
||||
'groups': groups,
|
||||
'unblock': is_need_unblock(key_block),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
Loading…
Reference in New Issue