mirror of https://github.com/jumpserver/jumpserver
commit
d1ccb4af15
|
@ -7,7 +7,7 @@ from rest_framework import status
|
|||
|
||||
from common.exceptions import JMSException
|
||||
from .signals import post_auth_failed
|
||||
from users.utils import LoginBlockUtil, MFABlockUtils
|
||||
from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil
|
||||
|
||||
reason_password_failed = 'password_failed'
|
||||
reason_password_decrypt_failed = 'password_decrypt_failed'
|
||||
|
@ -114,7 +114,18 @@ class AuthFailedError(Exception):
|
|||
return str(self.msg)
|
||||
|
||||
|
||||
class CredentialError(AuthFailedNeedLogMixin, AuthFailedNeedBlockMixin, AuthFailedError):
|
||||
class BlockGlobalIpLoginError(AuthFailedError):
|
||||
error = 'block_global_ip_login'
|
||||
|
||||
def __init__(self, username, ip, **kwargs):
|
||||
self.msg = _("IP is not allowed")
|
||||
LoginIpBlockUtil(ip).set_block_if_need()
|
||||
super().__init__(username=username, ip=ip, **kwargs)
|
||||
|
||||
|
||||
class CredentialError(
|
||||
AuthFailedNeedLogMixin, AuthFailedNeedBlockMixin, BlockGlobalIpLoginError, AuthFailedError
|
||||
):
|
||||
def __init__(self, error, username, ip, request):
|
||||
super().__init__(error=error, username=username, ip=ip, request=request)
|
||||
util = LoginBlockUtil(username, ip)
|
||||
|
@ -177,14 +188,6 @@ class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError):
|
|||
super().__init__(username=username, ip=ip)
|
||||
|
||||
|
||||
class BlockGlobalIpLoginError(AuthFailedError):
|
||||
error = 'block_global_ip_login'
|
||||
|
||||
def __init__(self, username, ip):
|
||||
self.msg = _("IP is not allowed")
|
||||
super().__init__(username=username, ip=ip)
|
||||
|
||||
|
||||
class SessionEmptyError(AuthFailedError):
|
||||
msg = session_empty_msg
|
||||
error = 'session_empty'
|
||||
|
|
|
@ -492,6 +492,7 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||
# 标记密码验证成功
|
||||
self.mark_password_ok(user=user, auto_login=auto_login)
|
||||
LoginBlockUtil(user.username, ip).clean_failed_count()
|
||||
LoginIpBlockUtil(ip).clean_block_if_need()
|
||||
return user
|
||||
|
||||
def mark_password_ok(self, user, auto_login=False):
|
||||
|
@ -517,6 +518,7 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||
self._check_login_acl(user, ip)
|
||||
|
||||
LoginBlockUtil(user.username, ip).clean_failed_count()
|
||||
LoginIpBlockUtil(ip).clean_block_if_need()
|
||||
MFABlockUtils(user.username, ip).clean_failed_count()
|
||||
|
||||
self.mark_password_ok(user, False)
|
||||
|
|
|
@ -3353,8 +3353,8 @@ msgid ""
|
|||
msgstr "单位:分, 当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
|
||||
|
||||
#: settings/serializers/security.py:61
|
||||
msgid "IP Black List"
|
||||
msgstr "IP 黑名单"
|
||||
msgid "Login IP Black List"
|
||||
msgstr "登录 IP 黑名单"
|
||||
|
||||
#: settings/serializers/security.py:64
|
||||
msgid ""
|
||||
|
|
|
@ -58,7 +58,7 @@ class SecurityAuthSerializer(serializers.Serializer):
|
|||
)
|
||||
)
|
||||
SECURITY_LOGIN_IP_BLACK_LIST = serializers.ListField(
|
||||
default=[], label=_('IP Black List'), allow_empty=True,
|
||||
default=[], label=_('Login IP Black List'), allow_empty=True,
|
||||
child=serializers.CharField(max_length=1024, validators=[ip_child_validator]),
|
||||
help_text=_(
|
||||
'Format for comma-delimited string. Such as: '
|
||||
|
|
|
@ -182,21 +182,32 @@ class BlockGlobalIpUtilBase:
|
|||
self.block_key = self.BLOCK_KEY_TMPL.format(ip)
|
||||
self.key_ttl = int(settings.SECURITY_LOGIN_LIMIT_TIME) * 60
|
||||
|
||||
def sign_limit_key_and_block_key(self):
|
||||
@property
|
||||
def ip_in_black_list(self):
|
||||
return self.ip in settings.SECURITY_LOGIN_IP_BLACK_LIST
|
||||
|
||||
def set_block_if_need(self):
|
||||
if not self.ip_in_black_list:
|
||||
return
|
||||
count = cache.get(self.limit_key, 0)
|
||||
count += 1
|
||||
cache.set(self.limit_key, count, self.key_ttl)
|
||||
|
||||
limit_count = settings.SECURITY_LOGIN_LIMIT_COUNT
|
||||
if count >= limit_count:
|
||||
cache.set(self.block_key, True, self.key_ttl)
|
||||
if count < limit_count:
|
||||
return
|
||||
cache.set(self.block_key, True, self.key_ttl)
|
||||
|
||||
def clean_block_if_need(self):
|
||||
if not self.ip_in_black_list:
|
||||
return
|
||||
cache.delete(self.limit_key)
|
||||
cache.delete(self.block_key)
|
||||
|
||||
def is_block(self):
|
||||
if self.ip in settings.SECURITY_LOGIN_IP_BLACK_LIST:
|
||||
self.sign_limit_key_and_block_key()
|
||||
return bool(cache.get(self.block_key))
|
||||
else:
|
||||
if not self.ip_in_black_list:
|
||||
return False
|
||||
return bool(cache.get(self.block_key))
|
||||
|
||||
|
||||
class LoginBlockUtil(BlockUtilBase):
|
||||
|
|
Loading…
Reference in New Issue