diff --git a/apps/common/forms.py b/apps/common/forms.py index 34337159f..d843eec0b 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -171,10 +171,11 @@ class SecuritySettingForm(BaseForm): initial=30, min_value=5, label=_("No logon interval"), help_text=_( - "Tip :(unit/minute) if the user has failed to log in for a limited " + "Tip: (unit/minute) if the user has failed to log in for a limited " "number of times, no login is allowed during this time interval." ) ) + # ssh max idle time SECURITY_MAX_IDLE_TIME = forms.IntegerField( initial=30, required=False, label=_("Connection max idle time"), @@ -183,6 +184,18 @@ class SecuritySettingForm(BaseForm): 'Unit: minute' ), ) + # password expiration time + SECURITY_PASSWORD_EXPIRATION_TIME = forms.IntegerField( + initial=9999, label=_("Password expiration time"), + min_value=1, + help_text=_( + "Tip: (unit/day) " + "If the user does not update the password during the time, " + "the user password will expire failure;" + "The password expiration reminder mail will be automatic sent to the user " + "by system within 5 days (daily) before the password expires" + ) + ) # min length SECURITY_PASSWORD_MIN_LENGTH = forms.IntegerField( initial=6, label=_("Password minimum length"), diff --git a/apps/common/models.py b/apps/common/models.py index a678d32dd..2e1790d4e 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -81,7 +81,9 @@ class Setting(models.Model): @classmethod def delete_storage(cls, name, storage_name): - obj = cls.objects.get(name=name) + obj = cls.objects.filter(name=name).first() + if not obj: + return False value = obj.cleaned_value value.pop(storage_name, '') obj.cleaned_value = value diff --git a/apps/common/templates/common/terminal_setting.html b/apps/common/templates/common/terminal_setting.html index d29de997f..0069c05a2 100644 --- a/apps/common/templates/common/terminal_setting.html +++ b/apps/common/templates/common/terminal_setting.html @@ -151,7 +151,7 @@ function deleteStorage($this, the_url){ toastr.success("{% trans 'Delete succeed' %}"); }; var error = function(){ - toastr.error("{% trans 'Delete failed' %}}"); + toastr.error("{% trans 'Delete failed' %}"); }; ajaxAPI(the_url, JSON.stringify(data), success, error, method); } diff --git a/apps/common/views.py b/apps/common/views.py index 524b6cbd4..e8ccfe6ba 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -26,7 +26,7 @@ class BasicSettingView(SuperUserRequiredMixin, TemplateView): form = self.form_class(request.POST) if form.is_valid(): form.save() - msg = _("Update setting successfully, please restart program") + msg = _("Update setting successfully") messages.success(request, msg) return redirect('settings:basic-setting') else: @@ -78,7 +78,7 @@ class LDAPSettingView(SuperUserRequiredMixin, TemplateView): form = self.form_class(request.POST) if form.is_valid(): form.save() - msg = _("Update setting successfully, please restart program") + msg = _("Update setting successfully") messages.success(request, msg) return redirect('settings:ldap-setting') else: @@ -109,7 +109,7 @@ class TerminalSettingView(SuperUserRequiredMixin, TemplateView): form = self.form_class(request.POST) if form.is_valid(): form.save() - msg = _("Update setting successfully, please restart program") + msg = _("Update setting successfully") messages.success(request, msg) return redirect('settings:terminal-setting') else: @@ -159,7 +159,7 @@ class SecuritySettingView(SuperUserRequiredMixin, TemplateView): form = self.form_class(request.POST) if form.is_valid(): form.save() - msg = _("Update setting successfully, please restart program") + msg = _("Update setting successfully") messages.success(request, msg) return redirect('settings:security-setting') else: diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 7433435e1..65ea4e26e 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -468,7 +468,8 @@ SECURITY_MFA_AUTH = False SECURITY_LOGIN_LIMIT_COUNT = 7 SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute SECURITY_MAX_IDLE_TIME = 30 # Unit: minute -SECURITY_PASSWORD_MIN_LENGTH = 6 +SECURITY_PASSWORD_EXPIRATION_TIME = 9999 # Unit: day +SECURITY_PASSWORD_MIN_LENGTH = 6 # Unit: bit SECURITY_PASSWORD_UPPER_CASE = False SECURITY_PASSWORD_LOWER_CASE = False SECURITY_PASSWORD_NUMBER = False diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 0f6f072a5..91637b223 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index c5ec299d2..6d0c8aa58 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: 2018-11-21 19:06+0800\n" +"POT-Creation-Date: 2018-11-22 15:16+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -191,9 +191,9 @@ msgstr "名称" #: assets/templates/assets/system_user_list.html:30 #: audits/templates/audits/login_log_list.html:49 #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:15 -#: users/forms.py:33 users/models/authentication.py:72 users/models/user.py:50 +#: users/forms.py:33 users/models/authentication.py:74 users/models/user.py:50 #: users/templates/users/_select_user_modal.html:14 -#: users/templates/users/login.html:62 +#: users/templates/users/login.html:64 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:24 #: users/templates/users/user_profile.html:47 @@ -206,7 +206,7 @@ msgstr "密码或密钥密码" #: assets/forms/user.py:26 assets/models/base.py:24 common/forms.py:105 #: users/forms.py:17 users/forms.py:35 users/forms.py:47 -#: users/templates/users/login.html:65 +#: users/templates/users/login.html:67 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 #: users/templates/users/user_password_authentication.html:18 @@ -431,10 +431,10 @@ msgstr "创建日期" #: perms/models.py:86 perms/templates/perms/asset_permission_detail.html:102 #: terminal/models.py:28 terminal/templates/terminal/terminal_detail.html:63 #: users/models/group.py:15 users/models/user.py:85 -#: users/templates/users/user_detail.html:123 +#: users/templates/users/user_detail.html:127 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:14 -#: users/templates/users/user_profile.html:130 xpack/plugins/cloud/models.py:45 +#: users/templates/users/user_profile.html:134 xpack/plugins/cloud/models.py:45 #: xpack/plugins/cloud/models.py:138 #: xpack/plugins/cloud/templates/cloud/account_detail.html:72 #: xpack/plugins/cloud/templates/cloud/account_list.html:15 @@ -487,7 +487,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:364 +#: users/models/user.py:397 msgid "System" msgstr "系统" @@ -620,8 +620,8 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/session_list.html:33 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:312 -#: users/models/user.py:32 users/models/user.py:352 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:310 +#: users/models/user.py:32 users/models/user.py:385 #: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:13 users/views/user.py:384 #: xpack/plugins/orgs/forms.py:26 @@ -862,9 +862,9 @@ msgstr "其它" #: terminal/templates/terminal/terminal_update.html:47 #: users/templates/users/_user.html:46 #: users/templates/users/user_bulk_update.html:23 -#: users/templates/users/user_detail.html:172 +#: users/templates/users/user_detail.html:176 #: users/templates/users/user_password_update.html:71 -#: users/templates/users/user_profile.html:198 +#: users/templates/users/user_profile.html:202 #: users/templates/users/user_profile_update.html:63 #: users/templates/users/user_pubkey_update.html:70 #: users/templates/users/user_pubkey_update.html:76 @@ -992,9 +992,9 @@ msgstr "测试" #: users/templates/users/user_group_detail.html:28 #: users/templates/users/user_group_list.html:43 #: users/templates/users/user_list.html:77 -#: users/templates/users/user_profile.html:151 -#: users/templates/users/user_profile.html:181 -#: users/templates/users/user_profile.html:190 +#: users/templates/users/user_profile.html:155 +#: users/templates/users/user_profile.html:185 +#: users/templates/users/user_profile.html:194 #: xpack/plugins/cloud/templates/cloud/account_detail.html:25 #: xpack/plugins/cloud/templates/cloud/account_list.html:38 #: xpack/plugins/orgs/templates/orgs/org_detail.html:25 @@ -1054,14 +1054,14 @@ msgstr "选择节点" #: assets/templates/assets/system_user_list.html:143 #: common/templates/common/terminal_setting.html:165 templates/_modal.html:22 #: terminal/templates/terminal/session_detail.html:108 -#: users/templates/users/user_detail.html:382 -#: users/templates/users/user_detail.html:408 -#: users/templates/users/user_detail.html:431 -#: users/templates/users/user_detail.html:476 +#: users/templates/users/user_detail.html:386 +#: users/templates/users/user_detail.html:412 +#: users/templates/users/user_detail.html:435 +#: users/templates/users/user_detail.html:480 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:88 #: users/templates/users/user_list.html:205 -#: users/templates/users/user_profile.html:232 +#: users/templates/users/user_profile.html:236 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:34 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:36 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:33 @@ -1128,8 +1128,8 @@ msgstr "创建日期" #: assets/templates/assets/asset_detail.html:141 #: terminal/templates/terminal/session_detail.html:81 -#: users/templates/users/user_detail.html:134 -#: users/templates/users/user_profile.html:142 +#: users/templates/users/user_detail.html:138 +#: users/templates/users/user_profile.html:146 msgid "Quick modify" msgstr "快速修改" @@ -1142,7 +1142,7 @@ msgstr "快速修改" #: perms/templates/perms/asset_permission_list.html:59 #: terminal/templates/terminal/terminal_list.html:34 #: users/templates/users/_select_user_modal.html:18 -#: users/templates/users/user_detail.html:140 +#: users/templates/users/user_detail.html:144 #: users/templates/users/user_granted_asset.html:46 #: users/templates/users/user_group_granted_asset.html:46 #: users/templates/users/user_list.html:28 @@ -1159,8 +1159,8 @@ msgid "Refresh" msgstr "刷新" #: assets/templates/assets/asset_detail.html:304 -#: users/templates/users/user_detail.html:301 -#: users/templates/users/user_detail.html:328 +#: users/templates/users/user_detail.html:305 +#: users/templates/users/user_detail.html:332 msgid "Update successfully!" msgstr "更新成功" @@ -1273,9 +1273,9 @@ msgstr "重命名失败,不能更改root节点的名称" #: assets/templates/assets/asset_list.html:627 #: assets/templates/assets/system_user_list.html:137 -#: users/templates/users/user_detail.html:376 -#: users/templates/users/user_detail.html:402 -#: users/templates/users/user_detail.html:470 +#: users/templates/users/user_detail.html:380 +#: users/templates/users/user_detail.html:406 +#: users/templates/users/user_detail.html:474 #: users/templates/users/user_group_list.html:82 #: users/templates/users/user_list.html:199 msgid "Are you sure?" @@ -1288,9 +1288,9 @@ msgstr "删除选择资产" #: assets/templates/assets/asset_list.html:631 #: assets/templates/assets/system_user_list.html:141 #: common/templates/common/terminal_setting.html:163 -#: users/templates/users/user_detail.html:380 -#: users/templates/users/user_detail.html:406 -#: users/templates/users/user_detail.html:474 +#: users/templates/users/user_detail.html:384 +#: users/templates/users/user_detail.html:410 +#: users/templates/users/user_detail.html:478 #: users/templates/users/user_group_create_update.html:31 #: users/templates/users/user_group_list.html:86 #: users/templates/users/user_list.html:203 @@ -1639,8 +1639,8 @@ msgid "Filename" msgstr "文件名" #: audits/models.py:22 audits/templates/audits/ftp_log_list.html:76 -#: ops/templates/ops/task_list.html:39 users/models/authentication.py:68 -#: users/templates/users/user_detail.html:452 xpack/plugins/cloud/api.py:61 +#: ops/templates/ops/task_list.html:39 users/models/authentication.py:70 +#: users/templates/users/user_detail.html:456 xpack/plugins/cloud/api.py:61 msgid "Success" msgstr "成功" @@ -1706,20 +1706,20 @@ msgstr "Agent" msgid "City" msgstr "城市" -#: audits/templates/audits/login_log_list.html:54 users/forms.py:169 -#: users/models/authentication.py:77 users/models/user.py:74 +#: audits/templates/audits/login_log_list.html:54 users/forms.py:168 +#: users/models/authentication.py:79 users/models/user.py:74 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" #: audits/templates/audits/login_log_list.html:55 -#: users/models/authentication.py:78 xpack/plugins/cloud/models.py:192 +#: users/models/authentication.py:80 xpack/plugins/cloud/models.py:192 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 msgid "Reason" msgstr "原因" #: audits/templates/audits/login_log_list.html:56 -#: users/models/authentication.py:79 xpack/plugins/cloud/models.py:191 +#: users/models/authentication.py:81 xpack/plugins/cloud/models.py:191 #: xpack/plugins/cloud/models.py:208 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:67 @@ -1758,7 +1758,7 @@ msgstr "改密日志" #: audits/views.py:187 templates/_nav.html:10 users/views/group.py:28 #: users/views/group.py:44 users/views/group.py:60 users/views/group.py:76 -#: users/views/group.py:92 users/views/login.py:332 users/views/user.py:68 +#: users/views/group.py:92 users/views/login.py:344 users/views/user.py:68 #: users/views/user.py:83 users/views/user.py:111 users/views/user.py:192 #: users/views/user.py:353 users/views/user.py:403 users/views/user.py:437 msgid "Users" @@ -1952,59 +1952,71 @@ msgstr "禁止登录时间间隔" #: common/forms.py:174 msgid "" -"Tip :(unit/minute) if the user has failed to log in for a limited number of " +"Tip: (unit/minute) if the user has failed to log in for a limited number of " "times, no login is allowed during this time interval." msgstr "" "提示: (单位: 分钟) 当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录." -#: common/forms.py:180 +#: common/forms.py:181 msgid "Connection max idle time" msgstr "SSH最大空闲时间" -#: common/forms.py:182 +#: common/forms.py:183 msgid "" "If idle time more than it, disconnect connection(only ssh now) Unit: minute" msgstr "提示: (单位: 分钟) 如果超过该配置没有操作,连接会被断开(仅ssh) " -#: common/forms.py:188 +#: common/forms.py:189 +msgid "Password expiration time" +msgstr "密码过期时间" + +#: common/forms.py:192 +msgid "" +"Tip: (unit/day) If the user does not update the password during the time, " +"the user password will expire failure;The password expiration reminder mail " +"will be automatic sent to the user by system within 5 days (daily) before " +"the password expires" +msgstr "" + +#: common/forms.py:201 msgid "Password minimum length" msgstr "密码最小长度 " -#: common/forms.py:194 +#: common/forms.py:207 msgid "Must contain capital letters" msgstr "必须包含大写字母" -#: common/forms.py:196 +#: common/forms.py:209 msgid "" "After opening, the user password changes and resets must contain uppercase " "letters" msgstr "开启后,用户密码修改、重置必须包含大写字母" -#: common/forms.py:202 +#: common/forms.py:215 msgid "Must contain lowercase letters" msgstr "必须包含小写字母" -#: common/forms.py:203 +#: common/forms.py:216 msgid "" "After opening, the user password changes and resets must contain lowercase " "letters" msgstr "开启后,用户密码修改、重置必须包含小写字母" -#: common/forms.py:209 +#: common/forms.py:222 msgid "Must contain numeric characters" msgstr "必须包含数字字符" -#: common/forms.py:210 +#: common/forms.py:223 msgid "" "After opening, the user password changes and resets must contain numeric " "characters" msgstr "开启后,用户密码修改、重置必须包含数字字符" -#: common/forms.py:216 +#: common/forms.py:229 msgid "Must contain special characters" msgstr "必须包含特殊字符" -#: common/forms.py:217 +#: common/forms.py:230 msgid "" "After opening, the user password changes and resets must contain special " "characters" @@ -2176,8 +2188,8 @@ msgstr "系统设置" #: common/views.py:29 common/views.py:55 common/views.py:81 common/views.py:112 #: common/views.py:162 -msgid "Update setting successfully, please restart program" -msgstr "更新设置成功, 请手动重启程序" +msgid "Update setting successfully" +msgstr "更新设置成功" #: common/views.py:127 msgid "Create replay storage" @@ -2445,9 +2457,9 @@ msgstr "组织管理" #: perms/forms.py:31 perms/models.py:30 perms/models.py:80 #: perms/templates/perms/asset_permission_list.html:55 #: perms/templates/perms/asset_permission_list.html:145 templates/_nav.html:14 -#: users/forms.py:282 users/models/group.py:26 users/models/user.py:58 +#: users/forms.py:280 users/models/group.py:26 users/models/user.py:58 #: users/templates/users/_select_user_modal.html:16 -#: users/templates/users/user_detail.html:207 +#: users/templates/users/user_detail.html:211 #: users/templates/users/user_list.html:26 #: xpack/plugins/orgs/templates/orgs/org_list.html:15 msgid "User group" @@ -2464,7 +2476,7 @@ msgstr "资产和节点至少选一个" #: perms/models.py:36 perms/models.py:83 #: perms/templates/perms/asset_permission_detail.html:90 #: users/models/user.py:90 users/templates/users/user_detail.html:107 -#: users/templates/users/user_profile.html:112 +#: users/templates/users/user_profile.html:116 msgid "Date expired" msgstr "失效日期" @@ -2493,7 +2505,7 @@ msgid "Add node to this permission" msgstr "添加节点" #: perms/templates/perms/asset_permission_asset.html:125 -#: users/templates/users/user_detail.html:224 +#: users/templates/users/user_detail.html:228 msgid "Join" msgstr "加入" @@ -2587,7 +2599,7 @@ msgstr "文档" msgid "Commercial support" msgstr "商业支持" -#: templates/_header_bar.html:89 templates/_nav_user.html:9 users/forms.py:148 +#: templates/_header_bar.html:89 templates/_nav_user.html:9 users/forms.py:147 #: users/templates/users/_user.html:39 #: users/templates/users/first_login.html:39 #: users/templates/users/user_password_update.html:40 @@ -2611,7 +2623,7 @@ msgid "Logout" msgstr "注销登录" #: templates/_header_bar.html:101 users/templates/users/login.html:46 -#: users/templates/users/login.html:70 +#: users/templates/users/login.html:72 msgid "Login" msgstr "登录" @@ -2619,7 +2631,41 @@ msgstr "登录" msgid "Dashboard" msgstr "仪表盘" -#: templates/_message.html:6 +#: templates/_message.html:7 +#, python-format +msgid "" +"\n" +" Your password has expired, please click this link update password.\n" +" " +msgstr "" +"\n" +" 您的密码已经过期,请点击 链接 更新密码\n" +" " + +#: templates/_message.html:14 +msgid "Your password will at" +msgstr "您的密码将于" + +#: templates/_message.html:14 +msgid "expired. " +msgstr "过期。" + +#: templates/_message.html:15 +#, python-format +msgid "" +"\n" +" please click this " +"link to update your password.\n" +" " +msgstr "" +"\n" +" 请点击 链接 更" +"新密码\n" +" " + +#: templates/_message.html:27 #, python-format msgid "" "\n" @@ -2632,7 +2678,7 @@ msgstr "" " 补充完整\n" " " -#: templates/_message.html:20 +#: templates/_message.html:40 #, python-format msgid "" "\n" @@ -3088,15 +3134,19 @@ msgstr "你可以使用ssh客户端工具连接终端" msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" -#: users/api/auth.py:82 +#: users/api/auth.py:67 +msgid "The user {} password has expired, please update." +msgstr "用户 {} 密码已经过期,请更新。" + +#: users/api/auth.py:92 msgid "Please carry seed value and conduct MFA secondary certification" msgstr "请携带seed值, 进行MFA二次认证" -#: users/api/auth.py:195 +#: users/api/auth.py:205 msgid "Please verify the user name and password first" msgstr "请先进行用户名和密码验证" -#: users/api/auth.py:207 +#: users/api/auth.py:217 msgid "MFA certification failed" msgstr "MFA认证失败" @@ -3167,11 +3217,11 @@ msgstr "MFA 验证码" msgid "Role" msgstr "角色" -#: users/forms.py:55 users/forms.py:228 +#: users/forms.py:55 users/forms.py:226 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:56 users/forms.py:229 +#: users/forms.py:56 users/forms.py:227 msgid "ssh-rsa AAAA..." msgstr "" @@ -3179,19 +3229,19 @@ msgstr "" msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:76 users/templates/users/user_detail.html:215 +#: users/forms.py:76 users/templates/users/user_detail.html:219 msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:110 users/forms.py:243 +#: users/forms.py:110 users/forms.py:241 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:114 users/forms.py:247 users/serializers.py:49 +#: users/forms.py:114 users/forms.py:245 users/serializers.py:49 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:154 +#: users/forms.py:153 msgid "" "Tip: when enabled, you will enter the MFA binding process the next time you " "log in. you can also directly bind in \"personal information -> quick " @@ -3200,11 +3250,11 @@ msgstr "" "提示:启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修" "改->更改MFA设置)中直接绑定!" -#: users/forms.py:164 +#: users/forms.py:163 msgid "* Enable MFA authentication to make the account more secure." msgstr "* 启用MFA认证,使账号更加安全." -#: users/forms.py:174 +#: users/forms.py:173 msgid "" "In order to protect you and your company, please keep your account, password " "and key sensitive information properly. (for example: setting complex " @@ -3213,41 +3263,41 @@ msgstr "" "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:" "设置复杂密码,启用MFA认证)" -#: users/forms.py:181 users/templates/users/first_login.html:48 +#: users/forms.py:180 users/templates/users/first_login.html:48 #: users/templates/users/first_login.html:107 #: users/templates/users/first_login.html:130 msgid "Finish" msgstr "完成" -#: users/forms.py:187 +#: users/forms.py:186 msgid "Old password" msgstr "原来密码" -#: users/forms.py:192 +#: users/forms.py:191 msgid "New password" msgstr "新密码" -#: users/forms.py:197 +#: users/forms.py:196 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:207 +#: users/forms.py:206 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:215 +#: users/forms.py:214 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:226 +#: users/forms.py:224 msgid "Automatically configure and download the SSH key" msgstr "自动配置并下载SSH密钥" -#: users/forms.py:230 +#: users/forms.py:228 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:258 users/models/user.py:82 +#: users/forms.py:256 users/models/user.py:82 #: users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:46 #: users/templates/users/user_profile.html:68 @@ -3256,7 +3306,7 @@ msgstr "复制你的公钥到这里" msgid "Public key" msgstr "ssh公钥" -#: users/forms.py:265 users/forms.py:270 users/forms.py:316 +#: users/forms.py:263 users/forms.py:268 users/forms.py:314 #: xpack/plugins/orgs/forms.py:30 msgid "Select users" msgstr "选择用户" @@ -3269,48 +3319,52 @@ msgstr "ssh密钥" msgid "Disabled" msgstr "禁用" -#: users/models/authentication.py:52 users/models/authentication.py:61 +#: users/models/authentication.py:52 users/models/authentication.py:62 msgid "-" msgstr "" -#: users/models/authentication.py:62 +#: users/models/authentication.py:63 msgid "Username/password check failed" msgstr "用户名/密码 校验失败" -#: users/models/authentication.py:63 +#: users/models/authentication.py:64 msgid "MFA authentication failed" msgstr "MFA 认证失败" -#: users/models/authentication.py:64 +#: users/models/authentication.py:65 msgid "Username does not exist" msgstr "用户名不存在" -#: users/models/authentication.py:69 xpack/plugins/cloud/models.py:184 +#: users/models/authentication.py:66 +msgid "Password expired" +msgstr "密码过期" + +#: users/models/authentication.py:71 xpack/plugins/cloud/models.py:184 #: xpack/plugins/cloud/models.py:198 msgid "Failed" msgstr "失败" -#: users/models/authentication.py:73 +#: users/models/authentication.py:75 msgid "Login type" msgstr "登录方式" -#: users/models/authentication.py:74 +#: users/models/authentication.py:76 msgid "Login ip" msgstr "登录IP" -#: users/models/authentication.py:75 +#: users/models/authentication.py:77 msgid "Login city" msgstr "登录城市" -#: users/models/authentication.py:76 +#: users/models/authentication.py:78 msgid "User agent" msgstr "Agent" -#: users/models/authentication.py:80 +#: users/models/authentication.py:82 msgid "Date login" msgstr "登录日期" -#: users/models/user.py:31 users/models/user.py:360 +#: users/models/user.py:31 users/models/user.py:393 msgid "Administrator" msgstr "管理员" @@ -3319,13 +3373,13 @@ msgid "Application" msgstr "应用程序" #: users/models/user.py:36 users/templates/users/user_profile.html:92 -#: users/templates/users/user_profile.html:163 -#: users/templates/users/user_profile.html:166 +#: users/templates/users/user_profile.html:167 +#: users/templates/users/user_profile.html:170 msgid "Disable" msgstr "禁用" #: users/models/user.py:37 users/templates/users/user_profile.html:90 -#: users/templates/users/user_profile.html:170 +#: users/templates/users/user_profile.html:174 msgid "Enable" msgstr "启用" @@ -3352,7 +3406,11 @@ msgstr "微信" msgid "Source" msgstr "用户来源" -#: users/models/user.py:363 +#: users/models/user.py:101 +msgid "Date password last updated" +msgstr "最后更新密码日期" + +#: users/models/user.py:396 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -3448,7 +3506,7 @@ msgstr "获取更多信息" #: users/templates/users/forgot_password.html:11 #: users/templates/users/forgot_password.html:27 -#: users/templates/users/login.html:81 +#: users/templates/users/login.html:83 msgid "Forgot password" msgstr "忘记密码" @@ -3493,15 +3551,19 @@ msgstr "" msgid "Changes the world, starting with a little bit." msgstr "改变世界,从一点点开始。" -#: users/templates/users/login.html:55 +#: users/templates/users/login.html:54 +msgid "The user password has expired" +msgstr "用户密码已过期" + +#: users/templates/users/login.html:57 msgid "Captcha invalid" msgstr "验证码错误" -#: users/templates/users/login.html:87 +#: users/templates/users/login.html:89 msgid "More login options" msgstr "更多登录方式" -#: users/templates/users/login.html:91 +#: users/templates/users/login.html:93 msgid "Keycloak" msgstr "" @@ -3565,7 +3627,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry" msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:367 users/utils.py:80 +#: users/templates/users/user_detail.html:371 users/utils.py:81 msgid "Reset password" msgstr "重置密码" @@ -3656,79 +3718,84 @@ msgstr "强制启用" msgid "Last login" msgstr "最后登录" -#: users/templates/users/user_detail.html:154 +#: users/templates/users/user_detail.html:123 +#: users/templates/users/user_profile.html:112 +msgid "Last password updated" +msgstr "最后更新密码" + +#: users/templates/users/user_detail.html:158 msgid "Force enabled MFA" msgstr "强制启用MFA" -#: users/templates/users/user_detail.html:169 +#: users/templates/users/user_detail.html:173 msgid "Reset MFA" msgstr "重置MFA" -#: users/templates/users/user_detail.html:177 +#: users/templates/users/user_detail.html:181 msgid "Send reset password mail" msgstr "发送重置密码邮件" -#: users/templates/users/user_detail.html:180 -#: users/templates/users/user_detail.html:188 +#: users/templates/users/user_detail.html:184 +#: users/templates/users/user_detail.html:192 msgid "Send" msgstr "发送" -#: users/templates/users/user_detail.html:185 +#: users/templates/users/user_detail.html:189 msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" -#: users/templates/users/user_detail.html:193 -#: users/templates/users/user_detail.html:455 +#: users/templates/users/user_detail.html:197 +#: users/templates/users/user_detail.html:459 msgid "Unblock user" msgstr "解除登录限制" -#: users/templates/users/user_detail.html:196 +#: users/templates/users/user_detail.html:200 msgid "Unblock" msgstr "解除" -#: users/templates/users/user_detail.html:310 +#: users/templates/users/user_detail.html:314 msgid "Goto profile page enable MFA" msgstr "请去个人信息页面启用自己的MFA" -#: users/templates/users/user_detail.html:366 +#: users/templates/users/user_detail.html:370 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:377 +#: users/templates/users/user_detail.html:381 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:392 +#: users/templates/users/user_detail.html:396 msgid "" "The reset-ssh-public-key E-mail has been sent successfully. Please inform " "the user to update his new ssh public key." msgstr "重设密钥邮件将会发送到用户邮箱" -#: users/templates/users/user_detail.html:393 +#: users/templates/users/user_detail.html:397 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:403 +#: users/templates/users/user_detail.html:407 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:421 -#: users/templates/users/user_profile.html:221 +#: users/templates/users/user_detail.html:425 +#: users/templates/users/user_profile.html:225 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:422 #: users/templates/users/user_detail.html:426 -#: users/templates/users/user_profile.html:222 -#: users/templates/users/user_profile.html:227 +#: users/templates/users/user_detail.html:430 +#: users/templates/users/user_profile.html:226 +#: users/templates/users/user_profile.html:231 msgid "User SSH public key update" msgstr "ssh密钥" -#: users/templates/users/user_detail.html:471 +#: users/templates/users/user_detail.html:475 msgid "After unlocking the user, the user can log in normally." msgstr "解除用户登录限制后,此用户即可正常登录" -#: users/templates/users/user_detail.html:485 +#: users/templates/users/user_detail.html:489 msgid "Reset user MFA success" msgstr "重置用户MFA成功" @@ -3826,32 +3893,32 @@ msgstr "安装完成后点击下一步进入绑定页面(如已安装,直接 msgid "Administrator Settings force MFA login" msgstr "管理员设置强制使用MFA登录" -#: users/templates/users/user_profile.html:116 users/views/user.py:229 +#: users/templates/users/user_profile.html:120 users/views/user.py:229 #: users/views/user.py:283 msgid "User groups" msgstr "用户组" -#: users/templates/users/user_profile.html:148 +#: users/templates/users/user_profile.html:152 msgid "Update password" msgstr "更改密码" -#: users/templates/users/user_profile.html:156 +#: users/templates/users/user_profile.html:160 msgid "Set MFA" msgstr "设置MFA" -#: users/templates/users/user_profile.html:178 +#: users/templates/users/user_profile.html:182 msgid "Update MFA" msgstr "更改MFA" -#: users/templates/users/user_profile.html:187 +#: users/templates/users/user_profile.html:191 msgid "Update SSH public key" msgstr "更改SSH密钥" -#: users/templates/users/user_profile.html:195 +#: users/templates/users/user_profile.html:199 msgid "Reset public key and download" msgstr "重置并下载SSH密钥" -#: users/templates/users/user_profile.html:225 +#: users/templates/users/user_profile.html:229 msgid "Failed to update SSH public key." msgstr "更新密钥失败" @@ -3881,11 +3948,11 @@ msgstr "新的公钥已设置成功,请下载对应的私钥" msgid "Update user" msgstr "更新用户" -#: users/utils.py:41 +#: users/utils.py:42 msgid "Create account successfully" msgstr "创建账户成功" -#: users/utils.py:43 +#: users/utils.py:44 #, python-format msgid "" "\n" @@ -3930,7 +3997,7 @@ msgstr "" "
\n" " " -#: users/utils.py:82 +#: users/utils.py:83 #, python-format msgid "" "\n" @@ -3974,11 +4041,64 @@ msgstr "" "
\n" " " -#: users/utils.py:113 +#: users/utils.py:114 +msgid "Security notice" +msgstr "安全通知" + +#: users/utils.py:116 +#, python-format +msgid "" +"\n" +" Hello %(name)s:\n" +"
\n" +" Your password will expire in %(date_password_expired)s,\n" +"
\n" +" For your account security, please click on the link below to update your " +"password in time\n" +"
\n" +" Click here update password\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" +"
Login direct\n" +"\n" +"
\n" +" " +msgstr "" +"\n" +" 您好 %(name)s:\n" +"
\n" +" 您的密码会在 %(date_password_expired)s 过期,\n" +"
\n" +" 为了您的账号安全,请点击下面的链接及时更新密码\n" +"
\n" +" 请点击这里更新密码\n" +"
\n" +" 如果您的密码已经过期,请点击 \n" +" 密码过期 \n" +" 申请一份重置密码邮件。\n" +"\n" +"
\n" +" ---\n" +"\n" +"
\n" +" 直接登录\n" +"\n" +"
\n" +" " + +#: users/utils.py:152 msgid "SSH Key Reset" msgstr "重置ssh密钥" -#: users/utils.py:115 +#: users/utils.py:154 #, python-format msgid "" "\n" @@ -4003,15 +4123,15 @@ msgstr "" "
\n" " " -#: users/utils.py:148 +#: users/utils.py:187 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:150 +#: users/utils.py:189 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:163 +#: users/utils.py:202 msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" @@ -4031,52 +4151,52 @@ msgstr "用户组授权资产" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:179 users/views/user.py:524 users/views/user.py:549 +#: users/views/login.py:191 users/views/user.py:524 users/views/user.py:549 msgid "MFA code invalid, or ntp sync server time" msgstr "MFA验证码不正确,或者服务器端时间不对" -#: users/views/login.py:211 +#: users/views/login.py:223 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:212 +#: users/views/login.py:224 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:228 +#: users/views/login.py:240 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:241 +#: users/views/login.py:253 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:242 +#: users/views/login.py:254 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:255 +#: users/views/login.py:267 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:256 +#: users/views/login.py:268 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:272 users/views/login.py:288 +#: users/views/login.py:284 users/views/login.py:300 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:284 +#: users/views/login.py:296 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:294 users/views/user.py:126 users/views/user.py:420 +#: users/views/login.py:306 users/views/user.py:126 users/views/user.py:420 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/login.py:332 +#: users/views/login.py:344 msgid "First login" msgstr "首次登陆" diff --git a/apps/templates/_message.html b/apps/templates/_message.html index 4c37cc138..3f13ff87b 100644 --- a/apps/templates/_message.html +++ b/apps/templates/_message.html @@ -1,4 +1,25 @@ {% load i18n %} + +{% block password_expired_message %} + {% url 'users:user-password-update' as user_password_update_url %} + {% if request.user.password_has_expired %} +
+ {% blocktrans %} + Your password has expired, please click this link update password. + {% endblocktrans %} + +
+ {% elif request.user.password_will_expired %} +
+ {% trans 'Your password will at' %} {{ request.user.date_password_expired }} {% trans 'expired. ' %} + {% blocktrans %} + please click this link to update your password. + {% endblocktrans %} + +
+ {% endif %} +{% endblock %} + {% block first_login_message %} {% if request.user.is_authenticated and request.user.is_first_login %}
@@ -6,7 +27,6 @@ {% blocktrans %} Your information was incomplete. Please click this link to complete your information. {% endblocktrans %} -
{% endif %} diff --git a/apps/users/api/auth.py b/apps/users/api/auth.py index 9de2be6b2..4f9d7cb1d 100644 --- a/apps/users/api/auth.py +++ b/apps/users/api/auth.py @@ -56,6 +56,19 @@ class UserAuthApi(RootOrgViewMixin, APIView): increase_login_failed_count(username, ip) return Response({'msg': msg}, status=401) + if user.password_has_expired: + data = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_PASSWORD_EXPIRED, + 'status': False + } + self.write_login_log(request, data) + msg = _("The user {} password has expired, please update.".format( + user.username)) + logger.info(msg) + return Response({'msg': msg}, status=401) + if not user.otp_enabled: data = { 'username': user.username, @@ -68,10 +81,7 @@ class UserAuthApi(RootOrgViewMixin, APIView): clean_failed_count(username, ip) token = generate_token(request, user) return Response( - { - 'token': token, - 'user': self.serializer_class(user).data - } + {'token': token, 'user': self.serializer_class(user).data} ) seed = uuid.uuid4().hex diff --git a/apps/users/forms.py b/apps/users/forms.py index df01fbdc1..8ebe81d5c 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -120,8 +120,7 @@ class UserCreateUpdateForm(OrgModelForm): public_key = self.cleaned_data.get('public_key') user = super().save(commit=commit) if password: - user.set_password(password) - user.save() + user.reset_password(password) if otp_level: user.otp_level = otp_level user.save() @@ -217,8 +216,7 @@ class UserPasswordForm(forms.Form): def save(self): password = self.cleaned_data['new_password'] - self.instance.set_password(password) - self.instance.save() + self.instance.reset_password(new_password=password) return self.instance diff --git a/apps/users/models/authentication.py b/apps/users/models/authentication.py index 5377b0f75..cd5d27c4c 100644 --- a/apps/users/models/authentication.py +++ b/apps/users/models/authentication.py @@ -56,12 +56,14 @@ class LoginLog(models.Model): REASON_PASSWORD = 1 REASON_MFA = 2 REASON_NOT_EXIST = 3 + REASON_PASSWORD_EXPIRED = 4 REASON_CHOICE = ( (REASON_NOTHING, _('-')), (REASON_PASSWORD, _('Username/password check failed')), (REASON_MFA, _('MFA authentication failed')), (REASON_NOT_EXIST, _("Username does not exist")), + (REASON_PASSWORD_EXPIRED, _("Password expired")), ) STATUS_CHOICE = ( diff --git a/apps/users/models/user.py b/apps/users/models/user.py index b7a2f7e6a..f0e0355a3 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -96,6 +96,10 @@ class User(AbstractUser): max_length=30, default=SOURCE_LOCAL, choices=SOURCE_CHOICES, verbose_name=_('Source') ) + date_password_last_updated = models.DateTimeField( + auto_now_add=True, blank=True, null=True, + verbose_name=_('Date password last updated') + ) def __str__(self): return '{0.name}({0.username})'.format(self) @@ -220,6 +224,34 @@ class User(AbstractUser): def is_staff(self, value): pass + @property + def is_local(self): + return self.source == self.SOURCE_LOCAL + + @property + def date_password_expired(self): + interval = settings.SECURITY_PASSWORD_EXPIRATION_TIME + date_expired = self.date_password_last_updated + timezone.timedelta( + days=int(interval)) + return date_expired + + @property + def password_expired_remain_days(self): + date_remain = self.date_password_expired - timezone.now() + return date_remain.days + + @property + def password_has_expired(self): + if self.is_local and self.password_expired_remain_days < 0: + return True + return False + + @property + def password_will_expired(self): + if self.is_local and self.password_expired_remain_days < 5: + return True + return False + def save(self, *args, **kwargs): if not self.name: self.name = self.username @@ -258,7 +290,7 @@ class User(AbstractUser): return False def check_public_key(self, public_key): - if self.ssH_public_key == public_key: + if self.ssh_public_key == public_key: return True return False @@ -340,6 +372,7 @@ class User(AbstractUser): def reset_password(self, new_password): self.set_password(new_password) + self.date_password_last_updated = timezone.now() self.save() def delete(self, using=None, keep_parents=False): diff --git a/apps/users/signals_handler.py b/apps/users/signals_handler.py index bae5e37cd..7473434d0 100644 --- a/apps/users/signals_handler.py +++ b/apps/users/signals_handler.py @@ -36,4 +36,3 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs): if user: user.source = user.SOURCE_LDAP user.save() - diff --git a/apps/users/tasks.py b/apps/users/tasks.py index 8d8eb6553..78a6bdc1a 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -2,10 +2,46 @@ # from celery import shared_task -from .utils import write_login_log + +from ops.celery.utils import ( + create_or_update_celery_periodic_tasks, + after_app_ready_start +) +from .models import User +from common.utils import get_logger +from .utils import write_login_log, send_password_expiration_reminder_mail + + +logger = get_logger(__file__) @shared_task def write_login_log_async(*args, **kwargs): write_login_log(*args, **kwargs) + +@shared_task +def check_password_expired(): + users = User.objects.exclude(role=User.ROLE_APP) + for user in users: + if not user.password_will_expired: + continue + + send_password_expiration_reminder_mail(user) + logger.info("The user {} password expires in {} days".format( + user, user.password_expired_remain_days) + ) + + +@shared_task +@after_app_ready_start +def check_password_expired_periodic(): + tasks = { + 'check_password_expired_periodic': { + 'task': check_password_expired.name, + 'interval': None, + 'crontab': '0 10 * * *', + 'enabled': True, + } + } + create_or_update_celery_periodic_tasks(tasks) diff --git a/apps/users/templates/users/login.html b/apps/users/templates/users/login.html index 6582dd447..3812599bc 100644 --- a/apps/users/templates/users/login.html +++ b/apps/users/templates/users/login.html @@ -50,6 +50,8 @@ {% if block_login %}

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

+ {% elif password_expired %} +

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

{% elif form.errors %} {% if 'captcha' in form.errors %}

{% trans 'Captcha invalid' %}

diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 64bab71d9..0827b9a1f 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -119,6 +119,10 @@ {% trans 'Last login' %}: {{ user_object.last_login|date:"Y-m-j H:i:s" }} + + {% trans 'Last password updated' %}: + {{ user_object.date_password_last_updated|date:"Y-m-j H:i:s" }} + {% trans 'Comment' %}: {{ user_object.comment }} diff --git a/apps/users/templates/users/user_profile.html b/apps/users/templates/users/user_profile.html index 3d22f3da9..a7e455976 100644 --- a/apps/users/templates/users/user_profile.html +++ b/apps/users/templates/users/user_profile.html @@ -108,6 +108,10 @@ {% trans 'Last login' %} {{ user.last_login|date:"Y-m-d H:i:s" }} + + {% trans 'Last password updated' %} + {{ user.date_password_last_updated|date:"Y-m-d H:i:s" }} + {% trans 'Date expired' %} {{ user.date_expired|date:"Y-m-d H:i:s" }} diff --git a/apps/users/utils.py b/apps/users/utils.py index 75c1c9b55..171f0a651 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -16,6 +16,7 @@ from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.auth import authenticate from django.utils.translation import ugettext as _ from django.core.cache import cache +from datetime import datetime from common.tasks import send_mail_async from common.utils import reverse, get_object_or_none @@ -109,6 +110,44 @@ def send_reset_password_mail(user): send_mail_async.delay(subject, message, recipient_list, html_message=message) +def send_password_expiration_reminder_mail(user): + subject = _('Security notice') + 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( + user.date_password_expired)).strftime('%Y-%m-%d %H:%M'), + 'update_password_url': reverse('users:user-password-update', external=True), + 'forget_password_url': reverse('users:forgot-password', external=True), + 'email': user.email, + 'login_url': reverse('users:login', external=True), + } + 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] diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 3901696ab..6bac250e0 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -68,7 +68,20 @@ class UserLoginView(FormView): if not self.request.session.test_cookie_worked(): return HttpResponse(_("Please enable cookies and try again.")) - set_tmp_user_to_cache(self.request, form.get_user()) + user = form.get_user() + + # user password expired + if user.password_has_expired: + data = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_PASSWORD_EXPIRED, + 'status': False + } + self.write_login_log(data) + return self.render_to_response(self.get_context_data(password_expired=True)) + + set_tmp_user_to_cache(self.request, user) username = form.cleaned_data.get('username') ip = get_request_ip(self.request) # 登陆成功,清除缓存计数 @@ -86,7 +99,6 @@ class UserLoginView(FormView): 'reason': reason, 'status': False } - self.write_login_log(data) # limit user login failed count diff --git a/requirements/deb_requirements.txt b/requirements/deb_requirements.txt index d4cf29941..07b3441c4 100644 --- a/requirements/deb_requirements.txt +++ b/requirements/deb_requirements.txt @@ -1 +1 @@ -libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake libkrb5-dev sshpass +libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite libkrb5-dev sshpass