Commit f2888319 authored by BaiJiangJie's avatar BaiJiangJie

[Update] Merge branch dev_beta to branch dev_beta_db

parents b265a386 8074e669
......@@ -212,12 +212,13 @@ class AssetsAmountMixin:
if cached is not None:
return cached
assets_amount = self.get_all_assets().count()
cache.set(cache_key, assets_amount, self.cache_time)
return assets_amount
@assets_amount.setter
def assets_amount(self, value):
self._assets_amount = value
cache_key = self._assets_amount_cache_key.format(self.key)
cache.set(cache_key, value, self.cache_time)
def expire_assets_amount(self):
ancestor_keys = self.get_ancestor_keys(with_self=True)
......
......@@ -59,6 +59,7 @@ class AuthSerializerMixin:
value = validated_data.get(field)
if not value:
validated_data.pop(field, None)
# print(validated_data)
# raise serializers.ValidationError(">>>>>>")
......
......@@ -3,6 +3,7 @@ from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from common.serializers import AdaptedBulkListSerializer
from common.utils import ssh_pubkey_gen
from orgs.mixins import BulkOrgResourceModelSerializer
from ..models import SystemUser
from .base import AuthSerializer, AuthSerializerMixin
......@@ -71,7 +72,9 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
super().validate_password(password)
auto_gen_key = self.initial_data.get("auto_generate_key", False)
private_key = self.initial_data.get("private_key")
if not self.instance and not auto_gen_key and not password and not private_key:
login_mode = self.initial_data.get("login_mode")
if not self.instance and not auto_gen_key and not password and \
not private_key and login_mode == SystemUser.LOGIN_AUTO:
raise serializers.ValidationError(_("Password or private key required"))
return password
......@@ -86,6 +89,13 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
private_key, public_key = SystemUser.gen_key(username)
attrs["private_key"] = private_key
attrs["public_key"] = public_key
# 如果设置了private key,没有设置public key则生成
elif attrs.get("private_key", None):
private_key = attrs["private_key"]
password = attrs.get("password")
public_key = ssh_pubkey_gen(private_key, password=password,
username=username)
attrs["public_key"] = public_key
attrs.pop("auto_generate_key", None)
return attrs
......
......@@ -112,6 +112,7 @@ def on_node_assets_changed(sender, instance=None, **kwargs):
@receiver(post_save, sender=Node)
def on_node_update_or_created(sender, instance=None, created=False, **kwargs):
if instance and not created:
Asset.expire_all_nodes_keys_cache()
instance.expire_full_value()
......
......@@ -94,10 +94,8 @@ urlpatterns = [
]
old_version_urlpatterns = [
re_path('(?P<resource>admin_user|system_user|domain|gateway|cmd-filter|asset-user)/.*', capi.redirect_plural_name_api)
re_path('(?P<resource>admin-user|system-user|domain|gateway|cmd-filter|asset-user)/.*', capi.redirect_plural_name_api)
]
urlpatterns += router.urls + old_version_urlpatterns
urlpatterns += router.urls + cmd_filter_router.urls
urlpatterns += router.urls + cmd_filter_router.urls + old_version_urlpatterns
from django.apps import AppConfig
from django.conf import settings
from django.db.models.signals import post_save
class AuditsConfig(AppConfig):
......@@ -6,3 +8,5 @@ class AuditsConfig(AppConfig):
def ready(self):
from . import signals_handler
if settings.SYSLOG_ENABLE:
post_save.connect(signals_handler.on_audits_log_create)
......@@ -3,11 +3,36 @@
from rest_framework import serializers
from .models import FTPLog
from terminal.models import Session
from . import models
class FTPLogSerializer(serializers.ModelSerializer):
class Meta:
model = FTPLog
model = models.FTPLog
fields = '__all__'
class LoginLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserLoginLog
fields = '__all__'
class OperateLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.OperateLog
fields = '__all__'
class PasswordChangeLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.PasswordChangeLog
fields = '__all__'
class SessionAuditSerializer(serializers.ModelSerializer):
class Meta:
model = Session
fields = '__all__'
......@@ -4,13 +4,18 @@
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.db import transaction
from rest_framework.renderers import JSONRenderer
from jumpserver.utils import current_request
from common.utils import get_request_ip, get_logger
from common.utils import get_request_ip, get_logger, get_syslogger
from users.models import User
from .models import OperateLog, PasswordChangeLog
from terminal.models import Session
from . import models
from . import serializers
logger = get_logger(__name__)
sys_logger = get_syslogger("audits")
json_render = JSONRenderer()
MODELS_NEED_RECORD = (
......@@ -36,7 +41,7 @@ def create_operate_log(action, sender, resource):
}
with transaction.atomic():
try:
OperateLog.objects.create(**data)
models.OperateLog.objects.create(**data)
except Exception as e:
logger.error("Create operate log error: {}".format(e))
......@@ -44,15 +49,15 @@ def create_operate_log(action, sender, resource):
@receiver(post_save, dispatch_uid="my_unique_identifier")
def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
if created:
action = OperateLog.ACTION_CREATE
action = models.OperateLog.ACTION_CREATE
else:
action = OperateLog.ACTION_UPDATE
action = models.OperateLog.ACTION_UPDATE
create_operate_log(action, sender, instance)
@receiver(post_delete, dispatch_uid="my_unique_identifier")
def on_object_delete(sender, instance=None, **kwargs):
create_operate_log(OperateLog.ACTION_DELETE, sender, instance)
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
@receiver(post_save, sender=User, dispatch_uid="my_unique_identifier")
......@@ -61,7 +66,32 @@ def on_user_change_password(sender, instance=None, **kwargs):
if not current_request or not current_request.user.is_authenticated:
return
with transaction.atomic():
PasswordChangeLog.objects.create(
models.PasswordChangeLog.objects.create(
user=instance, change_by=current_request.user,
remote_addr=get_request_ip(current_request),
)
def on_audits_log_create(sender, instance=None, **kwargs):
if sender == models.UserLoginLog:
category = "login_log"
serializer = serializers.LoginLogSerializer
elif sender == models.FTPLog:
serializer = serializers.FTPLogSerializer
category = "ftp_log"
elif sender == models.OperateLog:
category = "operation_log"
serializer = serializers.OperateLogSerializer
elif sender == models.PasswordChangeLog:
category = "password_change_log"
serializer = serializers.PasswordChangeLogSerializer
elif sender == Session:
category = "host_session_log"
serializer = serializers.SessionAuditSerializer
else:
return
s = serializer(instance=instance)
data = json_render.render(s.data).decode(errors='ignore')
msg = "{} - {}".format(category, data)
sys_logger.info(msg)
......@@ -5,6 +5,8 @@ from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField
from django.conf import settings
from users.utils import get_login_failed_count
class UserLoginForm(AuthenticationForm):
......@@ -16,10 +18,18 @@ class UserLoginForm(AuthenticationForm):
error_messages = {
'invalid_login': _(
"Please enter a correct username and password. Note that both "
"fields may be case-sensitive."
"The username or password you entered is incorrect, "
"please enter it again."
),
'inactive': _("This account is inactive."),
'limit_login': _(
"You can also try {times_try} times "
"(The account will be temporarily locked for {block_time} minutes)"
),
'block_login': _(
"The account has been locked "
"(please contact admin to unlock it or try again after {} minutes)"
)
}
def confirm_login_allowed(self, user):
......@@ -28,6 +38,25 @@ class UserLoginForm(AuthenticationForm):
self.error_messages['inactive'],
code='inactive',)
def get_limit_login_error_message(self, username, ip):
times_up = settings.SECURITY_LOGIN_LIMIT_COUNT
times_failed = get_login_failed_count(username, ip)
times_try = int(times_up) - int(times_failed)
block_time = settings.SECURITY_LOGIN_LIMIT_TIME
if times_try <= 0:
error_message = self.error_messages['block_login']
error_message = error_message.format(block_time)
else:
error_message = self.error_messages['limit_login']
error_message = error_message.format(
times_try=times_try, block_time=block_time,
)
return error_message
def add_limit_login_error(self, username, ip):
error = self.get_limit_login_error_message(username, ip)
self.add_error('password', error)
class UserLoginCaptchaForm(UserLoginForm):
captcha = CaptchaField()
......
......@@ -58,6 +58,7 @@
{% else %}
<p class="red-fonts">{{ form.non_field_errors.as_text }}</p>
{% endif %}
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
{% endif %}
<div class="form-group">
......@@ -78,10 +79,11 @@
{% endif %}
<div class="text-muted text-center">
<div>
<a href="{% url 'users:forgot-password' %}">
<small>{% trans 'Forgot password' %}?</small>
</a>
<div>
<a href="{% url 'users:forgot-password' %}">
<small>{% trans 'Forgot password' %}?</small>
</a>
</div>
</div>
{% if AUTH_OPENID %}
......
......@@ -72,9 +72,10 @@
<div class="contact-form col-md-10" style="margin-top: 10px;height: 35px">
<form id="contact-form" action="" method="post" role="form" novalidate="novalidate">
{% csrf_token %}
<div style="height: 45px;color: red;line-height: 17px;">
<div style="height: 70px;color: red;line-height: 17px;">
{% if block_login %}
<p class="red-fonts">{% trans 'Log in frequently and try again later' %}</p>
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
{% elif password_expired %}
<p class="red-fonts">{% trans 'The user password has expired' %}</p>
{% elif form.errors %}
......@@ -83,6 +84,7 @@
{% else %}
<p class="red-fonts">{{ form.non_field_errors.as_text }}</p>
{% endif %}
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
{% endif %}
</div>
......
......@@ -26,6 +26,7 @@ from users.utils import (
)
from ..signals import post_auth_success, post_auth_failed
from .. import forms
from .. import const
__all__ = [
......@@ -77,7 +78,7 @@ class UserLoginView(FormView):
user = form.get_user()
# user password expired
if user.password_has_expired:
reason = LoginLog.REASON_PASSWORD_EXPIRED
reason = const.password_expired
self.send_auth_signal(success=False, username=user.username, reason=reason)
return self.render_to_response(self.get_context_data(password_expired=True))
......@@ -92,10 +93,11 @@ class UserLoginView(FormView):
# write login failed log
username = form.cleaned_data.get('username')
exist = User.objects.filter(username=username).first()
reason = LoginLog.REASON_PASSWORD if exist else LoginLog.REASON_NOT_EXIST
reason = const.password_failed if exist else const.user_not_exist
# limit user login failed count
ip = get_request_ip(self.request)
increase_login_failed_count(username, ip)
form.add_limit_login_error(username, ip)
# show captcha
cache.set(self.key_prefix_captcha.format(ip), 1, 3600)
self.send_auth_signal(success=False, username=username, reason=reason)
......@@ -162,7 +164,7 @@ class UserLoginOtpView(FormView):
else:
self.send_auth_signal(
success=False, username=user.username,
reason=LoginLog.REASON_MFA
reason=const.mfa_failed
)
form.add_error(
'otp_code', _('MFA code invalid, or ntp sync server time')
......
......@@ -49,6 +49,8 @@ class IsOrgAdmin(IsValidUser):
"""Allows access only to superuser"""
def has_permission(self, request, view):
if not current_org:
return False
return super(IsOrgAdmin, self).has_permission(request, view) \
and current_org.can_admin_by(request.user)
......@@ -57,6 +59,8 @@ class IsOrgAdminOrAppUser(IsValidUser):
"""Allows access between superuser and app user"""
def has_permission(self, request, view):
if not current_org:
return False
return super(IsOrgAdminOrAppUser, self).has_permission(request, view) \
and (current_org.can_admin_by(request.user) or request.user.is_app)
......
......@@ -29,6 +29,6 @@ def send_mail_async(*args, **kwargs):
args = tuple(args)
try:
send_mail(*args, **kwargs)
return send_mail(*args, **kwargs)
except Exception as e:
logger.error("Sending mail error: {}".format(e))
......@@ -31,6 +31,10 @@ def get_logger(name=None):
return logging.getLogger('jumpserver.%s' % name)
def get_syslogger(name=None):
return logging.getLogger('jms.%s' % name)
def timesince(dt, since='', default="just now"):
"""
Returns string representing "time since" e.g.
......
......@@ -379,6 +379,8 @@ defaults = {
'ASSETS_PERM_CACHE_TIME': 3600*24,
'SECURITY_MFA_VERIFY_TTL': 3600,
'ASSETS_PERM_CACHE_ENABLE': False,
'SYSLOG_ADDR': '', # '192.168.0.1:514'
'SYSLOG_FACILITY': 'user',
}
......
......@@ -214,6 +214,9 @@ LOGGING = {
'simple': {
'format': '%(levelname)s %(message)s'
},
'syslog': {
'format': 'jumpserver: %(message)s'
},
'msg': {
'format': '%(message)s'
}
......@@ -246,19 +249,10 @@ LOGGING = {
'backupCount': 7,
'filename': ANSIBLE_LOG_FILE,
},
'gunicorn_file': {
'encoding': 'utf8',
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'msg',
'maxBytes': 1024*1024*100,
'backupCount': 2,
'filename': GUNICORN_LOG_FILE,
},
'gunicorn_console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'msg'
'syslog': {
'level': 'INFO',
'class': 'logging.NullHandler',
'formatter': 'syslog'
},
},
'loggers': {
......@@ -268,25 +262,17 @@ LOGGING = {
'level': LOG_LEVEL,
},
'django.request': {
'handlers': ['console', 'file'],
'handlers': ['console', 'file', 'syslog'],
'level': LOG_LEVEL,
'propagate': False,
},
'django.server': {
'handlers': ['console', 'file'],
'handlers': ['console', 'file', 'syslog'],
'level': LOG_LEVEL,
'propagate': False,
},
'jumpserver': {
'handlers': ['console', 'file'],
'level': LOG_LEVEL,
},
'jumpserver.users.api': {
'handlers': ['console', 'file'],
'level': LOG_LEVEL,
},
'jumpserver.users.view': {
'handlers': ['console', 'file'],
'handlers': ['console', 'file', 'syslog'],
'level': LOG_LEVEL,
},
'ops.ansible_api': {
......@@ -297,10 +283,10 @@ LOGGING = {
'handlers': ['console', 'file'],
'level': "INFO",
},
# 'gunicorn': {
# 'handlers': ['gunicorn_console', 'gunicorn_file'],
# 'level': 'INFO',
# },
'jms_audits': {
'handlers': ['syslog'],
'level': 'INFO'
},
# 'django.db': {
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG'
......@@ -308,6 +294,17 @@ LOGGING = {
}
}
SYSLOG_ENABLE = False
if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2:
host, port = CONFIG.SYSLOG_ADDR.split(':')
SYSLOG_ENABLE = True
LOGGING['handlers']['syslog'].update({
'class': 'logging.handlers.SysLogHandler',
'facility': CONFIG.SYSLOG_FACILITY,
'address': (host, int(port)),
})
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
# LANGUAGE_CODE = 'en'
......
This diff is collapsed.
......@@ -4,13 +4,14 @@ from rest_framework import viewsets
from django.db import transaction
from django.conf import settings
from orgs.mixins import RootOrgViewMixin
from common.permissions import IsValidUser
from ..models import CommandExecution
from ..serializers import CommandExecutionSerializer
from ..tasks import run_command_execution
class CommandExecutionViewSet(viewsets.ModelViewSet):
class CommandExecutionViewSet(RootOrgViewMixin, viewsets.ModelViewSet):
serializer_class = CommandExecutionSerializer
permission_classes = (IsValidUser,)
......
......@@ -109,7 +109,9 @@ def hello(name, callback=None):
# @after_app_shutdown_clean_periodic
# @register_as_period_task(interval=30)
def hello123():
p = subprocess.Popen('ls /tmp', shell=True)
print("{} Hello world".format(datetime.datetime.now().strftime("%H:%M:%S")))
return None
@shared_task
......
......@@ -608,8 +608,9 @@ jumpserver.initServerSideDataTable = function (options) {
var kv = val.split(":");
if (kv.length === 2) {
var value = kv[1];
value = value.replace("+", " ");
search_attr[kv[0]] = value
var key = kv[0].trim();
value = value.replace("+", " ").trim();
search_attr[key] = value
} else {
search_raw.push(kv)
}
......
......@@ -422,7 +422,7 @@
'minsLeft' : '剩余 $1 分钟', // from v2.1.17 added 13.11.2016
'openAsEncoding' : '使用所选编码重新打开', // from v2.1.19 added 2.12.2016
'saveAsEncoding' : '使用所选编码保存', // from v2.1.19 added 2.12.2016
'selectFolder' : '选择目录(暂不支持)', // from v2.1.20 added 13.12.2016
'selectFolder' : '选择目录', // from v2.1.20 added 13.12.2016
'firstLetterSearch': '首字母搜索', // from v2.1.23 added 24.3.2017
'presets' : '预置', // from v2.1.25 added 26.5.2017
'tooManyToTrash' : '项目太多,不能移动到回收站.', // from v2.1.25 added 9.6.2017
......
{% load i18n %}
{% block user_expired_message %}
{% if request.user.is_expired %}
<div class="alert alert-danger help-message alert-dismissable">
{% blocktrans %}
Your account has expired, please contact the administrator.
{% endblocktrans %}
<button aria-hidden="true" data-dismiss="alert" class="close" type="button" style="outline: none;">×</button>
</div>
{% elif request.user.will_expired %}
<div class="alert alert-danger help-message alert-dismissable">
{% trans 'Your account will at' %} {{ request.user.date_expired }} {% trans 'expired. ' %}
<button aria-hidden="true" data-dismiss="alert" class="close" type="button" style="outline: none;">×</button>
</div>
{% endif %}
{% endblock %}
{% block password_expired_message %}
{% url 'users:user-password-update' as user_password_update_url %}
{% if request.user.password_has_expired %}
{% if request.user.password_has_expired %}
<div class="alert alert-danger help-message alert-dismissable">
{% blocktrans %}
Your password has expired, please click <a href="{{ user_password_update_url }}"> this link </a> update password.
......
......@@ -27,6 +27,7 @@ class TerminalSerializer(serializers.ModelSerializer):
class SessionSerializer(BulkOrgResourceModelSerializer):
command_amount = serializers.IntegerField(read_only=True)
org_id = serializers.CharField(allow_blank=True)
class Meta:
model = Session
......
......@@ -207,20 +207,19 @@ class TokenMixin:
@property
def private_token(self):
from authentication.models import PrivateToken
try:
token = PrivateToken.objects.get(user=self)
except PrivateToken.DoesNotExist:
token = self.create_private_token()
return token
return self.create_private_token()
def create_private_token(self):
from authentication.models import PrivateToken
token = PrivateToken.objects.create(user=self)
token, created = PrivateToken.objects.get_or_create(user=self)
return token
def delete_private_token(self):
from authentication.models import PrivateToken
PrivateToken.objects.filter(user=self).delete()
def refresh_private_token(self):
self.private_token.delete()
self.delete_private_token()
return self.create_private_token()
def create_bearer_token(self, request=None):
......@@ -403,6 +402,18 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
else:
return False
@property
def expired_remain_days(self):
date_remain = self.date_expired - timezone.now()
return date_remain.days
@property
def will_expired(self):
if 0 <= self.expired_remain_days < 5:
return True
else:
return False
@property
def is_valid(self):
if self.is_active and not self.is_expired:
......@@ -412,7 +423,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
@property
def is_local(self):
return self.source == self.SOURCE_LOCAL
def save(self, *args, **kwargs):
if not self.name:
self.name = self.username
......
# -*- coding: utf-8 -*-
#
import datetime
from django.utils import timezone
from django.conf import settings
from celery import shared_task
from ops.celery.utils import create_or_update_celery_periodic_tasks
from ops.celery.decorator import after_app_ready_start, register_as_period_task
from ops.celery.decorator import after_app_ready_start
from common.utils import get_logger
from .models import User
from .utils import send_password_expiration_reminder_mail
from .utils import (
send_password_expiration_reminder_mail, send_user_expiration_reminder_mail
)
logger = get_logger(__file__)
......@@ -43,4 +42,27 @@ def check_password_expired_periodic():
create_or_update_celery_periodic_tasks(tasks)
@shared_task
def check_user_expired():
users = User.objects.exclude(role=User.ROLE_APP)
for user in users:
if not user.is_valid:
continue
if not user.will_expired:
continue
send_user_expiration_reminder_mail(user)
@shared_task
@after_app_ready_start
def check_user_expired_periodic():
tasks = {
'check_user_expired_periodic': {
'task': check_user_expired.name,
'interval': None,
'crontab': '0 14 * * *',
'enabled': True,
}
}
create_or_update_celery_periodic_tasks(tasks)
......@@ -85,20 +85,20 @@ def send_reset_password_mail(user):
recipient_list = [user.email]
message = _("""
Hello %(name)s:
</br>
<br>
Please click the link below to reset your password, if not your request, concern your account security
</br>
<br>
<a href="%(rest_password_url)s?token=%(rest_password_token)s">Click here reset password</a>
</br>
<br>
This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a>
</br>
<br>
---
</br>
<br>
<a href="%(login_url)s">Login direct</a>
</br>
<br>
""") % {
'name': user.name,
'rest_password_url': reverse('users:reset-password', external=True),
......@@ -118,24 +118,24 @@ def send_password_expiration_reminder_mail(user):
recipient_list = [user.email]
message = _("""
Hello %(name)s:
</br>
<br>
Your password will expire in %(date_password_expired)s,
</br>
<br>
For your account security, please click on the link below to update your password in time
</br>
<br>
<a href="%(update_password_url)s">Click here update password</a>
</br>
<br>
If your password has expired, please click
<a href="%(forget_password_url)s?email=%(email)s">Password expired</a>
to apply for a password reset email.
</br>
<br>
---
</br>
<br>
<a href="%(login_url)s">Login direct</a>
</br>
<br>
""") % {
'name': user.name,
'date_password_expired': datetime.fromtimestamp(datetime.timestamp(
......@@ -151,18 +151,39 @@ def send_password_expiration_reminder_mail(user):
send_mail_async.delay(subject, message, recipient_list, html_message=message)
def send_user_expiration_reminder_mail(user):
subject = _('Expiration notice')
recipient_list = [user.email]
message = _("""
Hello %(name)s:
<br>
Your account will expire in %(date_expired)s,
<br>
In order not to affect your normal work, please contact the administrator for confirmation.
<br>
""") % {
'name': user.name,
'date_expired': datetime.fromtimestamp(datetime.timestamp(
user.date_expired)).strftime('%Y-%m-%d %H:%M'),
}
if settings.DEBUG:
logger.debug(message)
send_mail_async.delay(subject, message, recipient_list, html_message=message)
def send_reset_ssh_key_mail(user):
subject = _('SSH Key Reset')
recipient_list = [user.email]
message = _("""
Hello %(name)s:
</br>
<br>
Your ssh public key has been reset by site administrator.
Please login and reset your ssh public key.
</br>
<br>
<a href="%(login_url)s">Login direct</a>
</br>
<br>
""") % {
'name': user.name,
'login_url': reverse('authentication:login', external=True),
......@@ -264,6 +285,12 @@ def increase_login_failed_count(username, ip):
cache.set(key_limit, count, int(limit_time)*60)
def get_login_failed_count(username, ip):
key_limit = key_prefix_limit.format(username, ip)
count = cache.get(key_limit, 0)
return count
def clean_failed_count(username, ip):
key_limit = key_prefix_limit.format(username, ip)
key_block = key_prefix_block.format(username)
......@@ -272,9 +299,8 @@ def clean_failed_count(username, ip):
def is_block_login(username, ip):
key_limit = key_prefix_limit.format(username, ip)
count = get_login_failed_count(username, ip)
key_block = key_prefix_block.format(username)
count = cache.get(key_limit, 0)
limit_count = settings.SECURITY_LOGIN_LIMIT_COUNT
limit_time = settings.SECURITY_LOGIN_LIMIT_TIME
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment