From 3d9c0a212efbb17a70e0729681b140f22aaf4c84 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Tue, 30 Jul 2019 19:10:06 +0800 Subject: [PATCH 1/4] =?UTF-8?q?[Update]=20=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=A4=B1=E8=B4=A5=E6=AC=A1=E6=95=B0=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/forms.py | 33 +++- .../templates/authentication/login.html | 10 +- .../templates/authentication/new_login.html | 4 +- apps/authentication/views/login.py | 1 + apps/locale/zh/LC_MESSAGES/django.mo | Bin 78311 -> 78676 bytes apps/locale/zh/LC_MESSAGES/django.po | 150 ++++++++++-------- apps/users/utils.py | 9 +- 7 files changed, 129 insertions(+), 78 deletions(-) diff --git a/apps/authentication/forms.py b/apps/authentication/forms.py index 61b073e21..5316e0d79 100644 --- a/apps/authentication/forms.py +++ b/apps/authentication/forms.py @@ -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() diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html index e565e209c..b31a716b8 100644 --- a/apps/authentication/templates/authentication/login.html +++ b/apps/authentication/templates/authentication/login.html @@ -58,6 +58,7 @@ {% else %}

{{ form.non_field_errors.as_text }}

{% endif %} +

{{ form.errors.password.as_text }}

{% endif %}
@@ -78,10 +79,11 @@ {% endif %}
-
- - {% trans 'Forgot password' %}? - +
+ + {% trans 'Forgot password' %}? + +
{% if AUTH_OPENID %} diff --git a/apps/authentication/templates/authentication/new_login.html b/apps/authentication/templates/authentication/new_login.html index c644e6fad..4a62dd6d0 100644 --- a/apps/authentication/templates/authentication/new_login.html +++ b/apps/authentication/templates/authentication/new_login.html @@ -72,9 +72,10 @@
{% csrf_token %} -
+
{% if block_login %}

{% trans 'Log in frequently and try again later' %}

+

{{ form.errors.password.as_text }}

{% elif password_expired %}

{% trans 'The user password has expired' %}

{% elif form.errors %} @@ -83,6 +84,7 @@ {% else %}

{{ form.non_field_errors.as_text }}

{% endif %} +

{{ form.errors.password.as_text }}

{% endif %}
diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 53112fcac..0939370f1 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -100,6 +100,7 @@ class UserLoginView(FormView): # 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) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 47239902980980e61e50183934a00b8144ea263b..098558e3f1dd022335dd3739c200d3209cf8e940 100644 GIT binary patch delta 23526 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 delta 23163 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 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 2e9883d6a..a7dd721b8 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-29 18:24+0800\n" +"POT-Creation-Date: 2019-07-30 18:48+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -429,29 +429,29 @@ msgstr "详情" #: assets/templates/assets/system_user_list.html:33 #: assets/templates/assets/system_user_list.html:85 audits/models.py:33 #: perms/templates/perms/asset_permission_detail.html:30 -#: perms/templates/perms/asset_permission_list.html:177 +#: perms/templates/perms/asset_permission_list.html:178 #: perms/templates/perms/remote_app_permission_detail.html:30 #: perms/templates/perms/remote_app_permission_list.html:59 #: terminal/templates/terminal/terminal_detail.html:16 -#: terminal/templates/terminal/terminal_list.html:72 +#: terminal/templates/terminal/terminal_list.html:73 #: users/templates/users/user_detail.html:25 #: users/templates/users/user_group_detail.html:28 #: users/templates/users/user_group_list.html:20 -#: users/templates/users/user_group_list.html:70 +#: users/templates/users/user_group_list.html:71 #: users/templates/users/user_list.html:20 -#: users/templates/users/user_list.html:102 -#: users/templates/users/user_list.html:105 +#: users/templates/users/user_list.html:103 +#: users/templates/users/user_list.html:106 #: users/templates/users/user_profile.html:181 #: users/templates/users/user_profile.html:191 #: users/templates/users/user_profile.html:201 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:29 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:55 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:56 #: xpack/plugins/cloud/templates/cloud/account_detail.html:23 -#: xpack/plugins/cloud/templates/cloud/account_list.html:39 +#: xpack/plugins/cloud/templates/cloud/account_list.html:40 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:29 -#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:56 +#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57 #: xpack/plugins/orgs/templates/orgs/org_detail.html:25 -#: xpack/plugins/orgs/templates/orgs/org_list.html:87 +#: xpack/plugins/orgs/templates/orgs/org_list.html:88 msgid "Update" msgstr "更新" @@ -473,25 +473,25 @@ msgstr "更新" #: assets/templates/assets/system_user_list.html:86 audits/models.py:34 #: ops/templates/ops/task_list.html:64 #: perms/templates/perms/asset_permission_detail.html:34 -#: perms/templates/perms/asset_permission_list.html:178 +#: perms/templates/perms/asset_permission_list.html:179 #: perms/templates/perms/remote_app_permission_detail.html:34 #: perms/templates/perms/remote_app_permission_list.html:60 #: settings/templates/settings/terminal_setting.html:93 #: settings/templates/settings/terminal_setting.html:115 -#: terminal/templates/terminal/terminal_list.html:74 +#: terminal/templates/terminal/terminal_list.html:75 #: users/templates/users/user_detail.html:30 #: users/templates/users/user_group_detail.html:32 -#: users/templates/users/user_group_list.html:72 -#: users/templates/users/user_list.html:110 -#: users/templates/users/user_list.html:114 +#: users/templates/users/user_group_list.html:73 +#: users/templates/users/user_list.html:111 +#: users/templates/users/user_list.html:115 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:33 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:57 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:58 #: xpack/plugins/cloud/templates/cloud/account_detail.html:27 -#: xpack/plugins/cloud/templates/cloud/account_list.html:41 +#: xpack/plugins/cloud/templates/cloud/account_list.html:42 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:33 -#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57 +#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58 #: xpack/plugins/orgs/templates/orgs/org_detail.html:29 -#: xpack/plugins/orgs/templates/orgs/org_list.html:89 +#: xpack/plugins/orgs/templates/orgs/org_list.html:90 msgid "Delete" msgstr "删除" @@ -719,9 +719,9 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: assets/templates/assets/domain_gateway_list.html:71 #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:52 audits/models.py:94 -#: audits/templates/audits/login_log_list.html:51 authentication/forms.py:11 -#: authentication/templates/authentication/login.html:64 -#: authentication/templates/authentication/new_login.html:90 +#: audits/templates/audits/login_log_list.html:51 authentication/forms.py:13 +#: authentication/templates/authentication/login.html:65 +#: authentication/templates/authentication/new_login.html:91 #: ops/models/adhoc.py:164 perms/templates/perms/asset_permission_list.html:70 #: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/remote_app_permission_user.html:54 @@ -748,9 +748,9 @@ msgstr "密码或密钥密码" #: assets/serializers/asset_user.py:62 #: assets/templates/assets/_asset_user_auth_update_modal.html:21 #: assets/templates/assets/_asset_user_auth_view_modal.html:27 -#: authentication/forms.py:13 -#: authentication/templates/authentication/login.html:67 -#: authentication/templates/authentication/new_login.html:93 +#: authentication/forms.py:15 +#: authentication/templates/authentication/login.html:68 +#: authentication/templates/authentication/new_login.html:94 #: settings/forms.py:110 users/forms.py:16 users/forms.py:28 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_password_authentication.html:18 @@ -1595,8 +1595,8 @@ msgstr "选择节点" #: users/templates/users/user_detail.html:441 #: users/templates/users/user_detail.html:486 #: users/templates/users/user_group_create_update.html:32 -#: users/templates/users/user_group_list.html:119 -#: users/templates/users/user_list.html:255 +#: users/templates/users/user_group_list.html:120 +#: users/templates/users/user_list.html:256 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:34 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:54 #: xpack/plugins/interface/templates/interface/interface.html:103 @@ -1649,10 +1649,10 @@ msgstr "创建管理用户" #: assets/templates/assets/asset_list.html:304 #: assets/templates/assets/system_user_list.html:192 #: assets/templates/assets/system_user_list.html:223 -#: users/templates/users/user_group_list.html:163 -#: users/templates/users/user_group_list.html:194 -#: users/templates/users/user_list.html:164 -#: users/templates/users/user_list.html:196 +#: users/templates/users/user_group_list.html:164 +#: users/templates/users/user_group_list.html:195 +#: users/templates/users/user_list.html:165 +#: users/templates/users/user_list.html:197 #: xpack/plugins/vault/templates/vault/vault.html:224 msgid "Please select file" msgstr "选择文件" @@ -1792,8 +1792,8 @@ msgstr "显示所有子节点资产" #: users/templates/users/user_detail.html:386 #: users/templates/users/user_detail.html:412 #: users/templates/users/user_detail.html:480 -#: users/templates/users/user_group_list.html:113 -#: users/templates/users/user_list.html:249 +#: users/templates/users/user_group_list.html:114 +#: users/templates/users/user_list.html:250 #: xpack/plugins/interface/templates/interface/interface.html:97 msgid "Are you sure?" msgstr "你确认吗?" @@ -1809,8 +1809,8 @@ msgstr "删除选择资产" #: users/templates/users/user_detail.html:416 #: users/templates/users/user_detail.html:484 #: users/templates/users/user_group_create_update.html:31 -#: users/templates/users/user_group_list.html:117 -#: users/templates/users/user_list.html:253 +#: users/templates/users/user_group_list.html:118 +#: users/templates/users/user_list.html:254 #: xpack/plugins/interface/templates/interface/interface.html:101 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:32 msgid "Cancel" @@ -2392,17 +2392,29 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "" -#: authentication/forms.py:19 +#: authentication/forms.py:21 msgid "" -"Please enter a correct username and password. Note that both fields may be " -"case-sensitive." -msgstr "请输入正确的用户名和密码. 注意它们是区分大小写." +"The username or password you entered is incorrect, please enter it again." +msgstr "您输入的用户名或密码不正确,请重新输入。" -#: authentication/forms.py:22 +#: authentication/forms.py:24 msgid "This account is inactive." msgstr "此账户无效" -#: authentication/forms.py:37 users/forms.py:22 +#: authentication/forms.py:26 +#, python-brace-format +msgid "" +"You can also try {times_try} times (The account will be temporarily locked " +"for {block_time} minutes)" +msgstr "您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)" + +#: authentication/forms.py:30 +msgid "" +"The account has been locked (please contact admin to unlock it or try again " +"after {} minutes)" +msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)" + +#: authentication/forms.py:66 users/forms.py:22 msgid "MFA code" msgstr "MFA 验证码" @@ -2466,8 +2478,8 @@ msgid "Changes the world, starting with a little bit." msgstr "改变世界,从一点点开始。" #: authentication/templates/authentication/login.html:46 -#: authentication/templates/authentication/login.html:72 -#: authentication/templates/authentication/new_login.html:99 +#: authentication/templates/authentication/login.html:73 +#: authentication/templates/authentication/new_login.html:100 #: templates/_header_bar.html:101 msgid "Login" msgstr "登录" @@ -2482,18 +2494,18 @@ msgstr "用户密码已过期" msgid "Captcha invalid" msgstr "验证码错误" -#: authentication/templates/authentication/login.html:83 -#: authentication/templates/authentication/new_login.html:103 +#: authentication/templates/authentication/login.html:84 +#: authentication/templates/authentication/new_login.html:104 #: users/templates/users/forgot_password.html:10 #: users/templates/users/forgot_password.html:25 msgid "Forgot password" msgstr "忘记密码" -#: authentication/templates/authentication/login.html:89 +#: authentication/templates/authentication/login.html:91 msgid "More login options" msgstr "更多登录方式" -#: authentication/templates/authentication/login.html:93 +#: authentication/templates/authentication/login.html:95 msgid "Keycloak" msgstr "" @@ -2542,16 +2554,16 @@ msgstr "欢迎回来,请输入用户名和密码登录" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:172 users/views/user.py:386 +#: authentication/views/login.py:173 users/views/user.py:386 #: users/views/user.py:411 msgid "MFA code invalid, or ntp sync server time" msgstr "MFA验证码不正确,或者服务器端时间不对" -#: authentication/views/login.py:203 +#: authentication/views/login.py:204 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:204 +#: authentication/views/login.py:205 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" @@ -2955,9 +2967,9 @@ msgstr "版本" #: ops/templates/ops/task_list.html:63 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:137 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:53 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:141 -#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:54 +#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55 msgid "Run" msgstr "执行" @@ -3167,7 +3179,7 @@ msgstr "刷新授权缓存" msgid "Validity" msgstr "有效" -#: perms/templates/perms/asset_permission_list.html:244 +#: perms/templates/perms/asset_permission_list.html:245 msgid "Refresh success" msgstr "刷新成功" @@ -4347,11 +4359,11 @@ msgstr "地址" msgid "Alive" msgstr "在线" -#: terminal/templates/terminal/terminal_list.html:77 +#: terminal/templates/terminal/terminal_list.html:78 msgid "Accept" msgstr "接受" -#: terminal/templates/terminal/terminal_list.html:79 +#: terminal/templates/terminal/terminal_list.html:80 msgid "Reject" msgstr "拒绝" @@ -4911,45 +4923,45 @@ msgstr "添加用户" msgid "Create user group" msgstr "创建用户组" -#: users/templates/users/user_group_list.html:114 +#: users/templates/users/user_group_list.html:115 msgid "This will delete the selected groups !!!" msgstr "删除选择组" -#: users/templates/users/user_group_list.html:123 +#: users/templates/users/user_group_list.html:124 msgid "UserGroups Deleted." msgstr "用户组删除" -#: users/templates/users/user_group_list.html:124 -#: users/templates/users/user_group_list.html:129 +#: users/templates/users/user_group_list.html:125 +#: users/templates/users/user_group_list.html:130 msgid "UserGroups Delete" msgstr "用户组删除" -#: users/templates/users/user_group_list.html:128 +#: users/templates/users/user_group_list.html:129 msgid "UserGroup Deleting failed." msgstr "用户组删除失败" -#: users/templates/users/user_list.html:250 +#: users/templates/users/user_list.html:251 msgid "This will delete the selected users !!!" msgstr "删除选中用户 !!!" -#: users/templates/users/user_list.html:266 +#: users/templates/users/user_list.html:267 msgid "User Deleted." msgstr "已被删除" -#: users/templates/users/user_list.html:267 -#: users/templates/users/user_list.html:271 +#: users/templates/users/user_list.html:268 +#: users/templates/users/user_list.html:272 msgid "User Delete" msgstr "删除" -#: users/templates/users/user_list.html:270 +#: users/templates/users/user_list.html:271 msgid "User Deleting failed." msgstr "用户删除失败" -#: users/templates/users/user_list.html:323 +#: users/templates/users/user_list.html:324 msgid "User is expired" msgstr "用户已失效" -#: users/templates/users/user_list.html:326 +#: users/templates/users/user_list.html:327 msgid "User is inactive" msgstr "用户已禁用" @@ -5484,7 +5496,7 @@ msgid "Run plan manually" msgstr "手动执行计划" #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:179 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:102 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:103 msgid "Execute failed" msgstr "执行失败" @@ -5708,7 +5720,7 @@ msgid "Run task manually" msgstr "手动执行任务" #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:181 -#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:98 +#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:99 msgid "Sync success" msgstr "同步成功" diff --git a/apps/users/utils.py b/apps/users/utils.py index 88564c2d3..3ccdc15b0 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -299,6 +299,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) @@ -307,9 +313,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 From 586d6e6abb6c782eb91fd03594e41fcbe8e74166 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 31 Jul 2019 16:57:21 +0800 Subject: [PATCH 2/4] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=BF=87=E6=9C=9F=E9=82=AE=E4=BB=B6=E6=8F=90=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 78676 -> 79397 bytes apps/locale/zh/LC_MESSAGES/django.po | 207 ++++++++++++++++----------- apps/templates/_message.html | 18 ++- apps/users/models/user.py | 14 +- apps/users/tasks.py | 32 ++++- apps/users/utils.py | 53 ++++--- 6 files changed, 217 insertions(+), 107 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 098558e3f1dd022335dd3739c200d3209cf8e940..2247e8f62680751357e3b8bf5a430219446218c9 100644 GIT binary patch delta 23965 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 W@=i)S-?gJ}Y@N7iTXevef&T|}n-I_d delta 23375 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 oJauBp|MOIue>$lp-8tEepKi9Xr\n" "Language-Team: Jumpserver team\n" @@ -167,7 +167,7 @@ msgstr "系统用户" #: settings/templates/settings/terminal_setting.html:105 terminal/models.py:22 #: terminal/models.py:258 terminal/templates/terminal/terminal_detail.html:43 #: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 -#: users/models/user.py:330 users/templates/users/_select_user_modal.html:13 +#: users/models/user.py:331 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_detail.html:63 #: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_list.html:35 @@ -218,7 +218,7 @@ msgstr "参数" #: perms/models/asset_permission.py:117 perms/models/base.py:41 #: perms/templates/perms/asset_permission_detail.html:98 #: perms/templates/perms/remote_app_permission_detail.html:90 -#: users/models/user.py:371 users/serializers/v1.py:120 +#: users/models/user.py:372 users/serializers/v1.py:120 #: users/templates/users/user_detail.html:111 #: xpack/plugins/change_auth_plan/models.py:106 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 @@ -279,7 +279,7 @@ msgstr "创建日期" #: perms/templates/perms/remote_app_permission_detail.html:94 #: settings/models.py:34 terminal/models.py:32 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 -#: users/models/user.py:363 users/templates/users/user_detail.html:129 +#: users/models/user.py:364 users/templates/users/user_detail.html:129 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:37 #: users/templates/users/user_profile.html:138 @@ -721,12 +721,12 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: assets/templates/assets/system_user_list.html:52 audits/models.py:94 #: audits/templates/audits/login_log_list.html:51 authentication/forms.py:13 #: authentication/templates/authentication/login.html:65 -#: authentication/templates/authentication/new_login.html:91 +#: authentication/templates/authentication/new_login.html:92 #: ops/models/adhoc.py:164 perms/templates/perms/asset_permission_list.html:70 #: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/remote_app_permission_user.html:54 #: settings/templates/settings/_ldap_list_users_modal.html:37 users/forms.py:14 -#: users/models/user.py:328 users/templates/users/_select_user_modal.html:14 +#: users/models/user.py:329 users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:36 #: users/templates/users/user_profile.html:47 @@ -750,7 +750,7 @@ msgstr "密码或密钥密码" #: assets/templates/assets/_asset_user_auth_view_modal.html:27 #: authentication/forms.py:15 #: authentication/templates/authentication/login.html:68 -#: authentication/templates/authentication/new_login.html:94 +#: authentication/templates/authentication/new_login.html:95 #: settings/forms.py:110 users/forms.py:16 users/forms.py:28 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_password_authentication.html:18 @@ -765,7 +765,7 @@ msgstr "密码" #: assets/forms/user.py:29 assets/serializers/asset_user.py:70 #: assets/templates/assets/_asset_user_auth_update_modal.html:27 -#: users/models/user.py:357 +#: users/models/user.py:358 msgid "Private key" msgstr "ssh私钥" @@ -971,7 +971,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:349 +#: assets/models/cluster.py:22 users/models/user.py:350 #: users/templates/users/user_detail.html:76 msgid "Phone" msgstr "手机" @@ -997,7 +997,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:457 +#: users/models/user.py:470 msgid "System" msgstr "系统" @@ -1116,7 +1116,7 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:65 #: terminal/templates/terminal/session_list.html:27 #: terminal/templates/terminal/session_list.html:71 users/forms.py:316 -#: users/models/user.py:127 users/models/user.py:445 +#: users/models/user.py:128 users/models/user.py:458 #: users/serializers/v1.py:109 users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:36 users/views/user.py:243 #: xpack/plugins/orgs/forms.py:26 @@ -1223,7 +1223,7 @@ msgid "Backend" msgstr "后端" #: assets/serializers/asset_user.py:66 users/forms.py:263 -#: users/models/user.py:360 users/templates/users/first_login.html:42 +#: users/models/user.py:361 users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:49 #: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile_update.html:46 @@ -1312,30 +1312,30 @@ msgstr "测试系统用户可连接性: {} => {}" msgid "Test system user connectivity period: {}" msgstr "定期测试系统用户可连接性: {}" -#: assets/tasks.py:471 assets/tasks.py:557 +#: assets/tasks.py:479 assets/tasks.py:565 #: xpack/plugins/change_auth_plan/models.py:522 msgid "The asset {} system platform {} does not support run Ansible tasks" msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务" -#: assets/tasks.py:483 +#: assets/tasks.py:491 msgid "" "Push system user task skip, auto push not enable or protocol is not ssh or " "rdp: {}" msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}" -#: assets/tasks.py:490 +#: assets/tasks.py:498 msgid "For security, do not push user {}" msgstr "为了安全,禁止推送用户 {}" -#: assets/tasks.py:518 assets/tasks.py:532 +#: assets/tasks.py:526 assets/tasks.py:540 msgid "Push system users to assets: {}" msgstr "推送系统用户到入资产: {}" -#: assets/tasks.py:524 +#: assets/tasks.py:532 msgid "Push system users to asset: {} => {}" msgstr "推送系统用户到入资产: {} => {}" -#: assets/tasks.py:604 +#: assets/tasks.py:612 msgid "Test asset user connectivity: {}" msgstr "测试资产用户可连接性: {}" @@ -2212,7 +2212,7 @@ msgstr "Agent" #: audits/models.py:99 audits/templates/audits/login_log_list.html:56 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms.py:175 users/models/user.py:352 +#: users/forms.py:175 users/models/user.py:353 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" @@ -2479,23 +2479,23 @@ msgstr "改变世界,从一点点开始。" #: authentication/templates/authentication/login.html:46 #: authentication/templates/authentication/login.html:73 -#: authentication/templates/authentication/new_login.html:100 +#: authentication/templates/authentication/new_login.html:101 #: templates/_header_bar.html:101 msgid "Login" msgstr "登录" #: authentication/templates/authentication/login.html:54 -#: authentication/templates/authentication/new_login.html:79 +#: authentication/templates/authentication/new_login.html:80 msgid "The user password has expired" msgstr "用户密码已过期" #: authentication/templates/authentication/login.html:57 -#: authentication/templates/authentication/new_login.html:82 +#: authentication/templates/authentication/new_login.html:83 msgid "Captcha invalid" msgstr "验证码错误" #: authentication/templates/authentication/login.html:84 -#: authentication/templates/authentication/new_login.html:104 +#: authentication/templates/authentication/new_login.html:105 #: users/templates/users/forgot_password.html:10 #: users/templates/users/forgot_password.html:25 msgid "Forgot password" @@ -3022,7 +3022,7 @@ msgstr "空" #: perms/templates/perms/asset_permission_list.html:118 #: perms/templates/perms/remote_app_permission_list.html:16 #: templates/_nav.html:14 users/forms.py:286 users/models/group.py:26 -#: users/models/user.py:336 users/templates/users/_select_user_modal.html:16 +#: users/models/user.py:337 users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_detail.html:217 #: users/templates/users/user_list.html:38 #: xpack/plugins/orgs/templates/orgs/org_list.html:15 @@ -3071,7 +3071,7 @@ msgstr "资产授权" #: perms/models/asset_permission.py:116 perms/models/base.py:40 #: perms/templates/perms/asset_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:82 -#: users/models/user.py:368 users/templates/users/user_detail.html:107 +#: users/models/user.py:369 users/templates/users/user_detail.html:107 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" @@ -3625,7 +3625,7 @@ msgid "Please submit the LDAP configuration before import" msgstr "请先提交LDAP配置再进行导入" #: settings/templates/settings/_ldap_list_users_modal.html:39 -#: users/models/user.py:332 users/templates/users/user_detail.html:71 +#: users/models/user.py:333 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" @@ -3893,7 +3893,24 @@ msgstr "下载导入模版" msgid "Select the CSV file to import" msgstr "请选择csv文件导入" -#: templates/_message.html:7 +#: templates/_message.html:6 +msgid "" +"\n" +" Your account has expired, please contact the administrator.\n" +" " +msgstr "" +"\n" +" 您的账户已经过期,请联系管理员。 " + +#: templates/_message.html:13 +msgid "Your account will at" +msgstr "您的账户将于" + +#: templates/_message.html:13 templates/_message.html:30 +msgid "expired. " +msgstr "过期。" + +#: templates/_message.html:23 #, python-format msgid "" "\n" @@ -3906,15 +3923,11 @@ msgstr "" "\"%(user_password_update_url)s\"> 链接 更新密码\n" " " -#: templates/_message.html:14 +#: templates/_message.html:30 msgid "Your password will at" msgstr "您的密码将于" -#: templates/_message.html:14 -msgid "expired. " -msgstr "过期。" - -#: templates/_message.html:15 +#: templates/_message.html:31 #, python-format msgid "" "\n" @@ -3927,7 +3940,7 @@ msgstr "" "新密码\n" " " -#: templates/_message.html:27 +#: templates/_message.html:43 #, python-format msgid "" "\n" @@ -3940,7 +3953,7 @@ msgstr "" " 补充完整\n" " " -#: templates/_message.html:40 +#: templates/_message.html:56 #, python-format msgid "" "\n" @@ -4408,7 +4421,7 @@ msgstr "你没有权限" msgid "Could not reset self otp, use profile reset instead" msgstr "不能再该页面重置MFA, 请去个人信息页面重置" -#: users/forms.py:33 users/models/user.py:340 +#: users/forms.py:33 users/models/user.py:341 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:37 @@ -4527,52 +4540,52 @@ msgstr "选择用户" msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/models/user.py:126 users/models/user.py:453 +#: users/models/user.py:127 users/models/user.py:466 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:128 +#: users/models/user.py:129 msgid "Application" msgstr "应用程序" -#: users/models/user.py:129 +#: users/models/user.py:130 msgid "Auditor" msgstr "审计员" -#: users/models/user.py:287 users/templates/users/user_profile.html:94 +#: users/models/user.py:288 users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 msgid "Disable" msgstr "禁用" -#: users/models/user.py:288 users/templates/users/user_profile.html:92 +#: users/models/user.py:289 users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" msgstr "启用" -#: users/models/user.py:289 users/templates/users/user_profile.html:90 +#: users/models/user.py:290 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:343 +#: users/models/user.py:344 msgid "Avatar" msgstr "头像" -#: users/models/user.py:346 users/templates/users/user_detail.html:82 +#: users/models/user.py:347 users/templates/users/user_detail.html:82 msgid "Wechat" msgstr "微信" -#: users/models/user.py:375 users/templates/users/user_detail.html:103 +#: users/models/user.py:376 users/templates/users/user_detail.html:103 #: users/templates/users/user_list.html:39 #: users/templates/users/user_profile.html:102 msgid "Source" msgstr "用户来源" -#: users/models/user.py:379 +#: users/models/user.py:380 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:456 +#: users/models/user.py:469 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -5115,43 +5128,43 @@ msgstr "您好 %(name)s" msgid "" "\n" " Hello %(name)s:\n" -"
\n" +"
\n" " Please click the link below to reset your password, if not your request, " "concern your account security\n" -"
\n" +"
\n" " Click " "here reset password\n" -"
\n" +"
\n" " This link is valid for 1 hour. After it expires, request new one\n" "\n" -"
\n" +"
\n" " ---\n" "\n" -"
\n" +"
\n" " Login direct\n" "\n" -"
\n" +"
\n" " " msgstr "" "\n" " 您好 %(name)s:\n" -"
\n" +"
\n" " 请点击下面链接重置密码, 如果不是您申请的,请关注账号安全\n" -"
\n" +"
\n" " 请点击这" "里设置密码 \n" -"
\n" +"
\n" " 这个链接有效期1小时, 超过时间您可以重新申请\n" "\n" -"
\n" +"
\n" " ---\n" "\n" -"
\n" +"
\n" " 直接登录\n" "\n" -"
\n" +"
\n" " " #: users/utils.py:121 @@ -5163,88 +5176,114 @@ msgstr "安全通知" msgid "" "\n" " Hello %(name)s:\n" -"
\n" +"
\n" " Your password will expire in %(date_password_expired)s,\n" -"
\n" +"
\n" " For your account security, please click on the link below to update your " "password in time\n" -"
\n" +"
\n" " Click here update password\n" -"
\n" +"
\n" " If your password has expired, please click \n" " Password expired \n" " to apply for a password reset email.\n" "\n" -"
\n" +"
\n" " ---\n" "\n" -"
\n" +"
\n" "
Login direct\n" "\n" -"
\n" +"
\n" " " msgstr "" "\n" " 您好 %(name)s:\n" -"
\n" +"
\n" " 您的密码会在 %(date_password_expired)s 过期,\n" -"
\n" +"
\n" " 为了您的账号安全,请点击下面的链接及时更新密码\n" -"
\n" +"
\n" " 请点击这里更新密码\n" -"
\n" +"
\n" " 如果您的密码已经过期,请点击 \n" " 密码过期 \n" " 申请一份重置密码邮件。\n" "\n" -"
\n" +"
\n" " ---\n" "\n" -"
\n" +"
\n" " 直接登录\n" "\n" -"
\n" +"
\n" " " #: users/utils.py:159 -msgid "SSH Key Reset" -msgstr "重置ssh密钥" +msgid "Expiration notice" +msgstr "过期通知" #: users/utils.py:161 #, python-format msgid "" "\n" +" Hello %(name)s:\n" +"
\n" +" Your account will expire in %(date_expired)s,\n" +"
\n" +" In order not to affect your normal work, please contact the " +"administrator for confirmation.\n" +"
\n" +" " +msgstr "" +"\n" +" 您好 %(name)s:\n" +"
\n" +" 您的账户会在 %(date_expired)s 过期,\n" +"
\n" +" 为了不影响您正常工作,请联系管理员确认。\n" +"
\n" +" " + +#: users/utils.py:180 +msgid "SSH Key Reset" +msgstr "重置ssh密钥" + +#: users/utils.py:182 +#, python-format +msgid "" +"\n" " Hello %(name)s:\n" -"
\n" +"
\n" " Your ssh public key has been reset by site administrator.\n" " Please login and reset your ssh public key.\n" -"
\n" +"
\n" " Login direct\n" "\n" -"
\n" +"
\n" " " msgstr "" "\n" " 你好 %(name)s:\n" -"
\n" +"
\n" " 您的密钥已被管理员重置,\n" " 请登录并重新设置您的密钥.\n" -"
\n" +"
\n" " Login direct\n" "\n" -"
\n" +"
\n" " " -#: users/utils.py:194 +#: users/utils.py:215 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:196 +#: users/utils.py:217 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:209 +#: users/utils.py:230 msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" diff --git a/apps/templates/_message.html b/apps/templates/_message.html index bdcbb6d5f..19e559d36 100644 --- a/apps/templates/_message.html +++ b/apps/templates/_message.html @@ -1,8 +1,24 @@ {% load i18n %} +{% block user_expired_message %} + {% if request.user.is_expired %} +
+ {% blocktrans %} + Your account has expired, please contact the administrator. + {% endblocktrans %} + +
+ {% elif request.user.will_expired %} +
+ {% trans 'Your account will at' %} {{ request.user.date_expired }} {% trans 'expired. ' %} + +
+ {% 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 %}
{% blocktrans %} Your password has expired, please click this link update password. diff --git a/apps/users/models/user.py b/apps/users/models/user.py index a6cdf9f0e..c2a568c34 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -402,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: @@ -411,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 diff --git a/apps/users/tasks.py b/apps/users/tasks.py index ab025cae3..cbdfd4848 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -1,16 +1,15 @@ # -*- 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) diff --git a/apps/users/utils.py b/apps/users/utils.py index 3ccdc15b0..f076e8df7 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -89,20 +89,20 @@ def send_reset_password_mail(user): recipient_list = [user.email] message = _(""" Hello %(name)s: -
+
Please click the link below to reset your password, if not your request, concern your account security -
+
Click here reset password -
+
This link is valid for 1 hour. After it expires, request new one -
+
--- -
+
Login direct -
+
""") % { 'name': user.name, 'rest_password_url': reverse('users:reset-password', external=True), @@ -122,24 +122,24 @@ def send_password_expiration_reminder_mail(user): recipient_list = [user.email] message = _(""" Hello %(name)s: -
+
Your password will expire in %(date_password_expired)s, -
+
For your account security, please click on the link below to update your password in time -
+
Click here update password -
+
If your password has expired, please click Password expired to apply for a password reset email. -
+
--- -
+
Login direct -
+
""") % { 'name': user.name, 'date_password_expired': datetime.fromtimestamp(datetime.timestamp( @@ -155,18 +155,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: +
+ Your account will expire in %(date_expired)s, +
+ In order not to affect your normal work, please contact the administrator for confirmation. +
+ """) % { + '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: -
+
Your ssh public key has been reset by site administrator. Please login and reset your ssh public key. -
+
Login direct -
+
""") % { 'name': user.name, 'login_url': reverse('authentication:login', external=True), From 42547751495fbc367658276525026b64d9c5666f Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Thu, 1 Aug 2019 17:10:02 +0800 Subject: [PATCH 3/4] Bugfix (#3065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] 修复浏览器关闭后session不失效的问题 * [Update] 修改一些内容 * [Update] 解决命令执行找不到对象的问题 * [Update] 修改Permission判断 * [Update] 修改session * [Update] 修改创建系统用户时没有public key --- apps/assets/models/node.py | 3 ++- apps/assets/serializers/base.py | 1 + apps/assets/serializers/system_user.py | 8 ++++++++ apps/common/permissions.py | 4 ++++ apps/jumpserver/middleware.py | 6 ++++-- apps/ops/api/command.py | 3 ++- apps/perms/apps.py | 3 +++ apps/terminal/serializers/v1.py | 1 + 8 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index d668cea51..916dae1e2 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -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) diff --git a/apps/assets/serializers/base.py b/apps/assets/serializers/base.py index 5e853219b..39e33ffe1 100644 --- a/apps/assets/serializers/base.py +++ b/apps/assets/serializers/base.py @@ -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(">>>>>>") diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 70855c9f7..912e085c0 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -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 @@ -86,6 +87,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 diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 8bea6b390..648689165 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -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) diff --git a/apps/jumpserver/middleware.py b/apps/jumpserver/middleware.py index f2ea1f077..c0472d64f 100644 --- a/apps/jumpserver/middleware.py +++ b/apps/jumpserver/middleware.py @@ -5,6 +5,7 @@ import re import pytz from django.utils import timezone from django.shortcuts import HttpResponse +from django.conf import settings from .utils import set_current_request @@ -56,6 +57,7 @@ class RequestMiddleware: def __call__(self, request): set_current_request(request) response = self.get_response(request) - age = request.session.get_expiry_age() - request.session.set_expiry(age) + if not settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: + age = request.session.get_expiry_age() + request.session.set_expiry(age) return response diff --git a/apps/ops/api/command.py b/apps/ops/api/command.py index ab5b97176..c63cbfdb8 100644 --- a/apps/ops/api/command.py +++ b/apps/ops/api/command.py @@ -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,) diff --git a/apps/perms/apps.py b/apps/perms/apps.py index 216e9c3d7..d6fa5f712 100644 --- a/apps/perms/apps.py +++ b/apps/perms/apps.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from django.conf import settings from django.apps import AppConfig @@ -8,4 +9,6 @@ class PermsConfig(AppConfig): def ready(self): from . import signals_handler + if not settings.XPACK_ENABLED: + settings.ASSETS_PERM_CACHE_ENABLE = False return super().ready() diff --git a/apps/terminal/serializers/v1.py b/apps/terminal/serializers/v1.py index 10898ebd6..e2807731c 100644 --- a/apps/terminal/serializers/v1.py +++ b/apps/terminal/serializers/v1.py @@ -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 From 2208320049ef271466301d7ae5658fcb915abce5 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 2 Aug 2019 19:49:25 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9elfinder=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/static/plugins/elfinder/i18n/elfinder.zh_CN.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/static/plugins/elfinder/i18n/elfinder.zh_CN.js b/apps/static/plugins/elfinder/i18n/elfinder.zh_CN.js index 44f5fe8dc..a80e7c4b5 100755 --- a/apps/static/plugins/elfinder/i18n/elfinder.zh_CN.js +++ b/apps/static/plugins/elfinder/i18n/elfinder.zh_CN.js @@ -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