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