From 812078331e75a2efe5bcec95ae4334c494ba6255 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 16 Jul 2018 12:13:13 +0800 Subject: [PATCH] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E8=A7=A3=E9=99=A4?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95=E9=99=90=E5=88=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36926 -> 36944 bytes apps/i18n/zh/LC_MESSAGES/django.po | 8 ++++---- apps/users/api.py | 7 ++++++- apps/users/templates/users/user_detail.html | 17 ++++++++++++----- apps/users/utils.py | 17 ++++++++++++++++- apps/users/views/login.py | 4 +++- apps/users/views/user.py | 10 ++++++++-- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 182ca35ceb4330dca81cc003be9451c796084219..50e22bf55f237bf8d2d1e9ddf1c548523ff9daf9 100644 GIT binary patch delta 4306 zcmYk;dvwor9LMo*yX7)A!;TWUEy<;noESBCDW{Z{ZmQ#6Q!djee91N|bIGw~6)74W zX{$+{h1y9smFOTH$w?-3RYo_f=WE~BAN}?CzTcnE=lywqKKp)uY+nsu14y!$W61CA;RO1aE3W8W{k2-lr+dcF{x*det_$KUu znT{8Ed^ze$pL6~d>`7eV_#|rMQ;y>v_Mgh8AVa|gsCi)l4?5Aab_1$`H|$pXp2v6E zFENREM^Ps_ZEI2M>rII^NV4rv`&{j~Ke8?iZu7vsHp|XJb(n8U>?(crztC}+-GWWX zZ^KY;|K~iY&?!%-#bo06>Cx4uqBd@Wn%B{B51WoE*dO($80q|I)ckRng3~;n=kcY} z`TMJIu@kFM3)b1!>~{MJ>ZAu8A4jdPar}?t`q_N@ z1tj!KScG*qg6jAbYCLX6bh0M4C907$RN<>p8}&hb??W&K@3UjD8}WEsgc-y;LO;Ym z!mkpE!8jfZQH6g;HSm|?^K*F7h#RBEH`p@lN4&-HANF5VqX{#k1Er$Ybwbq*ukb@( z4-9iW!trR-O^idGaHd`0@g=B2MfOEoX18Js#y>`Fe8lkyWL+4<%!(!?p*Fe{FT`}z zf=tu~_t*zeKe;)M=h?;f8N1$YM%ArCy%T#;KgIuIGW!Ryk4A5MGt}FghWf5AM=k8@ z@tZM?_O^;-u6mmD`OX(( zbMj@*zmIBUAF6>Pw$}Lz=W_q*yyM(xVmDNQbjKsGHSuWF@4_6^{O7Um>TS8Lv^!86 z??N5mAZp#usF$|}wZ1mwK@;oINNdy!p}2<|>xU{Z)cL#YM32uv6`JihA9X{E9haik zZ$j07*ZIS!eZpgY_#Jh{!Mw<1RG~Jg&U@hk%s?&NVfUcMzd^09#-3P%n%8B1)IcxP z=XWEjZVs|843=ii=ig$=0P1DLd9oL1!D6e z8`vhO@fN7BBn{&*-T4gEy5Xps8|nN6RQ+tniyc3c$Ng(!1qu0q{mK)6w11-hCzFsL zy+qwmjoe|!+v(Vt{5;g3WGN=#^R^VVZj-Il=c2@iPVDu>Zyg`EHQ1JUF$<%D9Z)Cg zj9Nbsb$}75U(&I7G0s4Jc173@H>19eFR?2&4hy1Rraq{GqfmvWqE3*Dskj1%;zk^V zr%`Wr?Lo5%=Jy48lU==pRa;%SA zJ^lfzvCr%w`x9#2IaDLbi=uh0Q2DN?e1;t!83uPcF~Mfr`PhjCOB}z2D)heN1GXB~ z&~K(&LV$|1Ds#xFuRvt9*Q&i#o_B&LeUr`&xE{VpQqT)8FLRUB* zY%@{y#$zj-f~va&Rqt8U`d2a3#O*xj9ry_A8bMv%e$>K4_N4P?Z7phk{L*NC5_Tp| z#je;N_0R4URGmUwf_gbim-4l$z;(-_0@qi3+`fEBvyye+uh?*`;LYz#-mczObfRQg eb-|X3`42X_xOvIC;Q0ErCz$a6p;b@55dR-#B4M-u delta 4288 zcmX}udvwor9LMo*yG1sWOIgbl5o)H+qIGw&NIIRQGfIv+-A5&>wpo2Gj4ea#bdicP zp&e(*tdvqmbU5lDCr2gSj+m~d+$!t&+UHe&JwEUE=ks}gF2C>hyKP^buzqpEs?n#JD~zHPti>#>!&KaYop2YX;X%~6W46VnObFxC>{<4FOlN$q;~|c3#W%kp%xtEc(^S<6}a1Rq2ppqBOjwSQtkZ9sD+l= zH=SQ^H`z}xo%N%gPW*tHc+eijOyZkw8>%N&Zu#jj{DdgRKbC$@xz=Si5fo!JL0|WFLD30 z$^8C0QI!+Vq9(jxU$d+22dI;N;&=~gev{+h9sh&<$R`!CdCWnLy9{-sgHf;f9Yx%~ z7Azx?hn3iRBd8tkL-ij+oh+si?yHQ5*Qx@n85Nal#X!f03=l!Q|^4|7;JVHhKbepwo)Oyl$wv@p*1s zlY1?m8esApm`>Qj6eJFtFqw6#Ie((v16px*1AsEHS$ z-i9IA3vYEi1^W=sMs2VWJK+XwJ*o3wJ3eR+*<;v6?|%!8uGnQtu)iH@$Dn+zH|P7{S#xIFrh4*unQ{gjao3zUT1Hy<5A-#q8_fPs0HSs zHuOAd{u1ZwF_XB_@eaGY%*ZETCtFR0C zTIb(E)!BmDz%JYDeEX-kf9*W;sW7kysz8q8e9R&qiTW-~LXBU5tygcC+IqVdweSYi z!M361eUEy0n^5zcV;UOxH)dg5e~|ikW0#-`T;cppcC7m!Koxq#aVhGC${jC6&0m75 zzsC8UsCDBn-Pn)1;y-NC)KI81YUkNF4RcWw*V;{}{?AbJzrcamgc^5FdDuWU>h-$@ zwZTGUUK~vijp!LWA2snsy9{-L2FD+xK14h1UfX2n~f=Y{|C{~ zf>+ra>}b@&_c$)LGn}9A_%+mms~vAZ-N-i7Nq6D>_?zR=T%+=nP(OE6MMD#6P#by4 zuCR@EgWYEL*e1M^c}HyS^ic0nRJ{^R#tPKU&BhDyd5kq+BMt3n2P!^@+R#yZ(k9Oc z{i&$8BmTktp?2ZqhUb{-{fh$mN$5zb7gm_l?G7Ugo(Fjzbdr&7R#xrmh4#QV59}l3O z?*5hGgqNZ=ay9aAV>AL^!1dS}3#vl@1WX|=!DNi5(nzFH;f^X)p=YrjzJhJB&i!jp z8(U|$+OJR>{|Pn!r1R~b3HeM^zQ4UZ7)QgLxYgceAI82+D093FwLpX8kL^y>hQ38@ z^mls#b<(8S!FIMIY9krgQSX0m8Y+;7ZSZO78jJc(??S%P(PT`;X4`_ApIXho zZ+JFpqXnos52E^u>~z%IQ=?e#e;o}Cd>2)Cquqfjv=>$2kn^X^32}Q=p)AL_b_lB8 zDC~ygP<6{t^(s;I7GbP`D{1H%cne!Mg1WkmsEJ$cZs+&epHbrvJN^eRAWoVap6&st zKfB{lbxQ3_)Wcabm$y{~vgd^b2Q_TzxoT*en)3ZMD}J2yc0+YxTKDvt^60?w+NS@L JZ&WQw{uiW`Uzz{_ diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 09b453262..9e3766748 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -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 "清除" diff --git a/apps/users/api.py b/apps/users/api.py index a3a613622..840b8c913 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -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: diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 0182cf521..a5adb31d2 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -182,7 +182,7 @@ - + {% trans 'Unblock user' %} @@ -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(); }); - }) {% endblock %} diff --git a/apps/users/utils.py b/apps/users/utils.py index 937a90867..7cbaa75f0 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -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 diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 2b86df19a..fc7caf305 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -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) diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 094d2ced2..56d551efa 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -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)